summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp4
-rw-r--r--CleanSpec.mk1
-rw-r--r--apct-tests/perftests/multiuser/Android.mk2
-rw-r--r--apct-tests/perftests/multiuser/AndroidManifest.xml2
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java3
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java9
-rw-r--r--api/current.txt74
-rw-r--r--api/system-current.txt268
-rw-r--r--api/test-current.txt19
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java40
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp55
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp24
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h3
-rw-r--r--cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl1
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h5
-rw-r--r--cmds/idmap2/include/idmap2/Result.h31
-rw-r--r--cmds/idmap2/include/idmap2/ZipFile.h4
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp44
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp10
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp11
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp9
-rw-r--r--cmds/idmap2/libidmap2/ZipFile.cpp6
-rw-r--r--cmds/idmap2/tests/ResourceUtilsTests.cpp15
-rw-r--r--cmds/idmap2/tests/ZipFileTests.cpp14
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp2
-rw-r--r--cmds/statsd/src/StatsService.cpp102
-rw-r--r--cmds/statsd/src/StatsService.h10
-rw-r--r--cmds/statsd/src/atoms.proto112
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp9
-rw-r--r--cmds/statsd/src/storage/StorageManager.h8
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp5
-rw-r--r--cmds/statsd/tests/StatsService_test.cpp39
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java48
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java19
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java2
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java71
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/AppOpsManager.java107
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java5
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/NotificationChannelGroup.java46
-rw-r--r--core/java/android/app/NotificationManager.java26
-rw-r--r--core/java/android/app/backup/BackupManager.java23
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl192
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java4
-rw-r--r--core/java/android/content/Intent.java5
-rw-r--r--core/java/android/content/pm/PackageInstaller.java80
-rw-r--r--core/java/android/content/pm/PackageManager.java13
-rw-r--r--core/java/android/hardware/display/BrightnessConfiguration.java329
-rw-r--r--core/java/android/hardware/display/BrightnessCorrection.aidl19
-rw-r--r--core/java/android/hardware/display/BrightnessCorrection.java245
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl4
-rw-r--r--core/java/android/hardware/usb/ParcelableUsbPort.aidl (renamed from core/java/android/hardware/usb/UsbPort.aidl)4
-rw-r--r--core/java/android/hardware/usb/ParcelableUsbPort.java87
-rw-r--r--core/java/android/hardware/usb/UsbManager.java72
-rw-r--r--core/java/android/hardware/usb/UsbPort.java186
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java165
-rw-r--r--core/java/android/net/INetdEventCallback.aidl14
-rw-r--r--core/java/android/net/InetAddresses.java63
-rw-r--r--core/java/android/net/NetworkCapabilities.java2
-rw-r--r--core/java/android/net/NetworkUtils.java5
-rw-r--r--core/java/android/os/FileUtils.java38
-rw-r--r--core/java/android/os/GraphicsEnvironment.java240
-rw-r--r--core/java/android/os/INetworkManagementService.aidl2
-rw-r--r--core/java/android/os/NativeHandle.java7
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java33
-rw-r--r--core/java/android/os/storage/StorageManager.java6
-rw-r--r--core/java/android/os/storage/StorageVolume.java27
-rw-r--r--core/java/android/permission/RuntimePermissionPresenter.java321
-rw-r--r--core/java/android/provider/DocumentsContract.java13
-rw-r--r--core/java/android/provider/MediaStore.java3
-rw-r--r--core/java/android/provider/Settings.java65
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureEventsRequest.java20
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java182
-rw-r--r--core/java/android/service/contentcapture/IContentCaptureService.aidl12
-rw-r--r--core/java/android/service/quicksettings/Tile.java20
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java19
-rw-r--r--core/java/android/util/FeatureFlagUtils.java1
-rw-r--r--core/java/android/view/NotificationHeaderView.java24
-rw-r--r--core/java/android/view/TouchDelegate.java5
-rw-r--r--core/java/android/view/contentcapture/ActivityContentCaptureSession.java471
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java27
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java10
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java383
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl30
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl10
-rw-r--r--core/java/android/webkit/WebSyncManager.java1
-rw-r--r--core/java/com/android/internal/app/ColorDisplayController.java26
-rw-r--r--core/java/com/android/internal/app/IAppOpsNotedCallback.aidl22
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl6
-rw-r--r--core/java/com/android/internal/app/procstats/AssociationState.java45
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java6
-rw-r--r--core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java (renamed from services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java)2
-rw-r--r--core/java/com/android/internal/infra/AbstractRemoteService.java (renamed from services/core/java/com/android/server/infra/AbstractRemoteService.java)16
-rw-r--r--core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java (renamed from services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java)2
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl3
-rw-r--r--core/java/com/android/internal/usb/DumpUtils.java12
-rw-r--r--core/java/com/android/internal/widget/NumericTextView.java2
-rw-r--r--core/java/com/android/server/net/BaseNetdEventCallback.java6
-rw-r--r--core/jni/Android.bp3
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp18
-rw-r--r--core/jni/android/graphics/Graphics.cpp30
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h3
-rw-r--r--core/jni/android_graphics_Canvas.cpp5
-rw-r--r--core/jni/android_media_AudioSystem.cpp30
-rw-r--r--core/jni/android_os_Debug.cpp137
-rw-r--r--core/jni/android_util_Process.cpp78
-rw-r--r--core/jni/android_view_DisplayListCanvas.cpp6
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp10
-rw-r--r--core/proto/android/service/procstats.proto4
-rw-r--r--core/res/AndroidManifest.xml15
-rw-r--r--core/res/res/layout/notification_template_header.xml1
-rw-r--r--core/res/res/values-night/themes_permission_controller.xml3
-rw-r--r--core/res/res/values/config.xml20
-rw-r--r--core/res/res/values/public.xml5
-rw-r--r--core/res/res/values/styles_permission_controller.xml45
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/res/res/values/themes_device_defaults.xml9
-rw-r--r--core/res/res/values/themes_permission_controller.xml4
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java22
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java13
-rw-r--r--core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java16
-rw-r--r--core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk2
-rw-r--r--core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml2
-rw-r--r--core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java23
-rw-r--r--core/tests/packagemanagertests/Android.mk2
-rw-r--r--core/tests/packagemanagertests/AndroidManifest.xml2
-rw-r--r--core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java5
-rw-r--r--core/tests/privacytests/Android.mk2
-rw-r--r--core/tests/privacytests/AndroidManifest.xml2
-rw-r--r--core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java7
-rw-r--r--core/tests/privacytests/src/android/privacy/RapporEncoderTest.java5
-rw-r--r--core/tests/utiltests/Android.mk2
-rw-r--r--core/tests/utiltests/AndroidManifest.xml2
-rwxr-xr-xcore/tests/utiltests/runtests.sh2
-rw-r--r--core/tests/utiltests/src/android/util/IntArrayTest.java5
-rw-r--r--core/tests/utiltests/src/android/util/LongArrayTest.java5
-rw-r--r--core/tests/utiltests/src/android/util/MemoryIntArrayTest.java5
-rw-r--r--core/tests/utiltests/src/android/util/RemoteIntArray.java3
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java9
-rw-r--r--data/etc/Android.bp64
-rw-r--r--data/etc/Android.mk76
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--data/fonts/fonts.xml2
-rw-r--r--graphics/java/android/graphics/Canvas.java4
-rw-r--r--graphics/java/android/graphics/RecordingCanvas.java10
-rw-r--r--libs/androidfw/Android.bp1
-rw-r--r--libs/androidfw/LoadedArsc.cpp2
-rw-r--r--libs/hwui/Android.bp4
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp3
-rw-r--r--libs/hwui/RecordingCanvas.cpp20
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/hwui/RenderNode.cpp5
-rw-r--r--libs/hwui/SkiaCanvas.cpp6
-rw-r--r--libs/hwui/SkiaCanvas.h1
-rw-r--r--libs/hwui/WebViewFunctorManager.cpp167
-rw-r--r--libs/hwui/WebViewFunctorManager.h92
-rw-r--r--libs/hwui/hwui/Bitmap.cpp85
-rw-r--r--libs/hwui/hwui/Bitmap.h44
-rw-r--r--libs/hwui/hwui/Canvas.h4
-rw-r--r--libs/hwui/pipeline/skia/FunctorDrawable.h36
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp41
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h6
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp20
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.h4
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp7
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp16
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h8
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp4
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h4
-rw-r--r--libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp22
-rw-r--r--libs/hwui/pipeline/skia/SkiaMemoryTracer.h4
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp9
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h3
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp33
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h1
-rw-r--r--libs/hwui/pipeline/skia/SkiaUtils.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp4
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp37
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.h15
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp78
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h12
-rw-r--r--libs/hwui/private/hwui/WebViewFunctor.h82
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp3
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp9
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp5
-rw-r--r--libs/hwui/renderthread/RenderThread.h4
-rw-r--r--libs/hwui/surfacetexture/ImageConsumer.cpp3
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp20
-rw-r--r--libs/hwui/tests/common/TestUtils.h62
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp2
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp2
-rw-r--r--libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp2
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp58
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp3
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp99
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp27
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp38
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp6
-rw-r--r--libs/hwui/tests/unit/VectorDrawableTests.cpp25
-rw-r--r--libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp154
-rw-r--r--libs/hwui/tests/unit/main.cpp4
-rw-r--r--libs/hwui/utils/Color.h3
-rw-r--r--libs/input/PointerController.cpp136
-rw-r--r--libs/input/PointerController.h12
-rw-r--r--libs/input/SpriteController.cpp24
-rw-r--r--libs/input/SpriteController.h8
-rw-r--r--location/tests/locationtests/Android.mk2
-rw-r--r--location/tests/locationtests/AndroidManifest.xml2
-rw-r--r--location/tests/locationtests/AndroidTest.xml2
-rw-r--r--media/java/android/media/AudioRecordingConfiguration.java168
-rw-r--r--media/java/android/media/AudioSystem.java19
-rw-r--r--media/java/android/media/FileDataSourceDesc.java59
-rw-r--r--media/java/android/media/MediaItem2.java324
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java8
-rw-r--r--media/java/android/media/MediaPlayer.java32
-rw-r--r--media/java/android/media/MediaPlayer2.java166
-rw-r--r--media/java/android/media/MediaPlayerBase.java331
-rw-r--r--media/java/android/media/ThumbnailUtils.java566
-rw-r--r--media/jni/Android.bp3
-rw-r--r--media/jni/android_media_MediaPlayer.cpp50
-rw-r--r--media/jni/android_media_MediaPlayer2.cpp6
-rw-r--r--native/android/net.c8
-rw-r--r--packages/CarSystemUI/Android.bp2
-rw-r--r--packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml26
-rw-r--r--packages/CarSystemUI/res-keyguard/drawable/ic_done.xml26
-rw-r--r--packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml31
-rw-r--r--packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml82
-rw-r--r--packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml126
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml32
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml31
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml37
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml104
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml84
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml127
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml83
-rw-r--r--packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml19
-rw-r--r--packages/CarSystemUI/res-keyguard/values-land/dimens.xml20
-rw-r--r--packages/CarSystemUI/res-keyguard/values/colors.xml21
-rw-r--r--packages/CarSystemUI/res-keyguard/values/dimens.xml30
-rw-r--r--packages/CarSystemUI/res-keyguard/values/integers.xml20
-rw-r--r--packages/CarSystemUI/res-keyguard/values/styles.xml53
-rw-r--r--packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml8
-rw-r--r--packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml4
-rw-r--r--packages/CarSystemUI/res/layout/car_left_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_qs_footer.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_qs_panel.xml5
-rw-r--r--packages/CarSystemUI/res/layout/car_right_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/values/config.xml1
-rw-r--r--packages/CarSystemUI/res/values/themes.xml24
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java60
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java13
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java42
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java14
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java18
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java9
-rw-r--r--packages/CompanionDeviceManager/AndroidManifest.xml1
-rw-r--r--packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java45
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java5
-rw-r--r--packages/MtpDocumentsProvider/perf_tests/Android.mk2
-rw-r--r--packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml2
-rw-r--r--packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java16
-rw-r--r--packages/PackageInstaller/res/values/strings.xml2
-rw-r--r--packages/PrintSpooler/tests/outofprocess/Android.bp2
-rw-r--r--packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml2
-rw-r--r--packages/PrintSpooler/tests/outofprocess/AndroidTest.xml2
-rw-r--r--packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java3
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/BarChartPreference/Android.bp13
-rw-r--r--packages/SettingsLib/BarChartPreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml65
-rw-r--r--packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml58
-rw-r--r--packages/SettingsLib/BarChartPreference/res/values/attrs.xml25
-rw-r--r--packages/SettingsLib/BarChartPreference/res/values/dimens.xml21
-rw-r--r--packages/SettingsLib/BarChartPreference/res/values/styles.xml46
-rw-r--r--packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java197
-rw-r--r--packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java103
-rw-r--r--packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java101
-rw-r--r--packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java189
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java27
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java22
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java28
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java93
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java225
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/docs/dagger.md204
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java8
-rw-r--r--packages/SystemUI/proguard.flags7
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml36
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml16
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml16
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java30
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java456
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyProvider.java569
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java119
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java246
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java634
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java162
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java332
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java290
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java208
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java167
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java212
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java89
-rw-r--r--packages/overlays/AccentColorBlackOverlay/Android.mk1
-rw-r--r--packages/overlays/AccentColorGreenOverlay/Android.mk1
-rw-r--r--packages/overlays/AccentColorPurpleOverlay/Android.mk1
-rw-r--r--packages/overlays/CleanSpec.mk53
-rw-r--r--packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk2
-rw-r--r--packages/overlays/IconShapeRoundedRectOverlay/Android.mk1
-rw-r--r--packages/overlays/IconShapeSquareOverlay/Android.mk1
-rw-r--r--packages/overlays/IconShapeSquircleOverlay/Android.mk1
-rw-r--r--packages/overlays/IconShapeTeardropOverlay/Android.mk1
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto15
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java2
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java455
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java228
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java34
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java23
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java90
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java33
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java25
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java18
-rw-r--r--services/core/java/com/android/server/AppOpsService.java214
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java7
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java1
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java14
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java4
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java40
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java184
-rw-r--r--services/core/java/com/android/server/am/ConnectionRecord.java6
-rw-r--r--services/core/java/com/android/server/am/ContentProviderConnection.java8
-rw-r--r--services/core/java/com/android/server/am/ContentProviderRecord.java2
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/am/OWNERS2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java10
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java42
-rw-r--r--services/core/java/com/android/server/audio/RecordingActivityMonitor.java39
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java6
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java13
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java199
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java124
-rw-r--r--services/core/java/com/android/server/display/ColorDisplayService.java270
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java96
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java9
-rw-r--r--services/core/java/com/android/server/display/PersistentDataStore.java95
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java40
-rw-r--r--services/core/java/com/android/server/infra/ServiceNameResolver.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java135
-rw-r--r--services/core/java/com/android/server/job/JobConcurrencyManager.java92
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java7
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java120
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java33
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java7
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java38
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java12
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java24
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java22
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerSettings.java31
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java11
-rw-r--r--services/core/java/com/android/server/pm/dex/PackageDexUsage.java34
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java9
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java11
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java342
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java25
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java28
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java34
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java33
-rw-r--r--services/core/java/com/android/server/wm/OWNERS2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java18
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java129
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java13
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp90
-rw-r--r--services/java/com/android/server/SystemServer.java41
-rw-r--r--services/net/java/android/net/dhcp/DhcpServer.java15
-rw-r--r--services/net/java/android/net/ip/IpServer.java12
-rw-r--r--services/robotests/Android.mk14
-rw-r--r--services/robotests/backup/Android.mk84
-rw-r--r--services/robotests/backup/config/robolectric.properties1
-rw-r--r--services/robotests/backup/src/android/app/backup/BackupUtilsTest.java (renamed from services/robotests/src/android/app/backup/BackupUtilsTest.java)0
-rw-r--r--services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java (renamed from services/robotests/src/android/app/backup/ForwardingBackupAgent.java)0
-rw-r--r--services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java (renamed from services/robotests/src/com/android/commands/bmgr/BmgrTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java (renamed from services/robotests/src/com/android/commands/bu/AdbBackupTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java (renamed from services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java (renamed from services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java1536
-rw-r--r--services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java (renamed from services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java (renamed from services/robotests/src/com/android/server/backup/TransportManagerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java (renamed from services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java)19
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java (renamed from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java (renamed from services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java (renamed from services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java (renamed from services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java (renamed from services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java (renamed from services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java (renamed from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java (renamed from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java)3
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java (renamed from services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java (renamed from services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java (renamed from services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java (renamed from services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java (renamed from services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java (renamed from services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java (renamed from services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java)12
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java (renamed from services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/PackageData.java (renamed from services/robotests/src/com/android/server/backup/testing/PackageData.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java (renamed from services/robotests/src/com/android/server/backup/testing/TestUtils.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/TransportData.java (renamed from services/robotests/src/com/android/server/backup/testing/TransportData.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java (renamed from services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/Utils.java (renamed from services/robotests/src/com/android/server/backup/testing/Utils.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java (renamed from services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java (renamed from services/robotests/src/com/android/server/backup/transport/TransportClientTest.java)0
-rw-r--r--services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java (renamed from services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java)0
-rw-r--r--services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java744
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java30
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java400
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java59
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java80
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java61
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java178
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java10
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsXmlV1.java8
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java35
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java56
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java54
-rw-r--r--startop/view_compiler/dex_builder.cc17
-rw-r--r--startop/view_compiler/dex_builder.h25
-rw-r--r--startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java20
-rw-r--r--startop/view_compiler/dex_testcase_generator.cc13
-rw-r--r--telecomm/java/android/telecom/PhoneAccountSuggestion.aidl22
-rw-r--r--telecomm/java/android/telecom/PhoneAccountSuggestion.java16
-rw-r--r--telecomm/java/android/telecom/PhoneAccountSuggestionService.java123
-rw-r--r--telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl26
-rw-r--r--telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl28
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java36
-rw-r--r--telephony/java/android/telephony/CellIdentityGsm.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java2
-rw-r--r--telephony/java/android/telephony/DataFailCause.java496
-rw-r--r--telephony/java/android/telephony/DisconnectCause.java42
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java41
-rw-r--r--telephony/java/android/telephony/PreciseCallState.java119
-rw-r--r--telephony/java/android/telephony/PreciseDisconnectCause.java314
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java64
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java40
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java117
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl1
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyPermissions.java29
-rw-r--r--tests/net/java/android/net/dhcp/DhcpServerTest.java7
-rw-r--r--tests/net/java/android/net/ip/IpServerTest.java2
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java2
-rw-r--r--tests/testables/src/android/testing/BaseFragmentTest.java16
-rw-r--r--tools/aapt2/ResourceParser.cpp101
-rw-r--r--tools/aapt2/ResourceParser_test.cpp144
-rw-r--r--tools/aapt2/ResourceTable.cpp20
-rw-r--r--tools/aapt2/ResourceTable.h32
-rw-r--r--tools/aapt2/ResourceTable_test.cpp90
-rw-r--r--tools/aapt2/Resources.proto27
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp49
-rw-r--r--tools/aapt2/format/binary/BinaryResourceParser.cpp22
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp18
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp101
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp93
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp74
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp86
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp4
-rw-r--r--tools/aapt2/link/TableMerger.cpp10
-rw-r--r--tools/aapt2/link/TableMerger_test.cpp75
-rw-r--r--tools/aapt2/split/TableSplitter.cpp2
-rw-r--r--tools/aapt2/test/Builders.cpp2
-rw-r--r--tools/aapt2/test/Builders.h2
-rw-r--r--tools/apilint/apilint.py105
-rwxr-xr-xtools/apilint/apilint_sha_system.sh23
-rw-r--r--tools/apilint/apilint_test.py147
-rw-r--r--tools/incident_section_gen/main.cpp2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java65
-rw-r--r--wifi/tests/Android.mk2
-rw-r--r--wifi/tests/AndroidManifest.xml2
-rw-r--r--wifi/tests/AndroidTest.xml2
-rw-r--r--wifi/tests/README.md2
-rwxr-xr-xwifi/tests/runtests.sh2
-rw-r--r--wifi/tests/src/android/net/wifi/ParcelUtilTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/ScanResultTest.java5
-rw-r--r--wifi/tests/src/android/net/wifi/WifiConfigurationTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java6
-rw-r--r--wifi/tests/src/android/net/wifi/WifiInfoTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java6
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiScannerTest.java4
-rw-r--r--wifi/tests/src/android/net/wifi/WifiSsidTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java7
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java5
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java4
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java7
-rw-r--r--wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java3
662 files changed, 19847 insertions, 7856 deletions
diff --git a/Android.bp b/Android.bp
index 1b81306b3699..4e7a7b4d7ae2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -357,6 +357,7 @@ java_defaults {
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
"core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+ "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
"core/java/android/view/contentcapture/IContentCaptureManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
"core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
@@ -387,6 +388,7 @@ java_defaults {
"core/java/android/speech/tts/ITextToSpeechService.aidl",
"core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
"core/java/com/android/internal/app/IAppOpsCallback.aidl",
+ "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl",
"core/java/com/android/internal/app/IAppOpsService.aidl",
"core/java/com/android/internal/app/IBatteryStats.aidl",
"core/java/com/android/internal/app/ISoundTriggerService.aidl",
@@ -519,6 +521,8 @@ java_defaults {
"telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
"telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
+ "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
+ "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
"telephony/java/android/telephony/data/IDataService.aidl",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 478e4fe86d3b..d01e183df84d 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -251,6 +251,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 5ff4ebc0eb80..5852044effa8 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
apct-perftests-utils
LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index adb316fe6c43..e96771cf1c17 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -25,7 +25,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.perftests.multiuser"/>
</manifest>
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index d3a3ce54e378..ba33e6439fbd 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -18,9 +18,10 @@ package android.multiuser;
import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 855be0859520..2fdba0af2c1b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -27,9 +27,10 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
@@ -50,7 +51,7 @@ import java.util.concurrent.TimeUnit;
* adb install -r \
* ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
* adb shell am instrument -e class android.multiuser.UserLifecycleTests \
- * -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ * -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner
*
* or
*
diff --git a/api/current.txt b/api/current.txt
index da888d7a3c02..96485a81fffb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5730,7 +5730,6 @@ package android.app {
public final class NotificationChannelGroup implements android.os.Parcelable {
ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
- method public boolean canOverlayApps();
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
@@ -5738,7 +5737,6 @@ package android.app {
method public java.lang.String getId();
method public java.lang.CharSequence getName();
method public boolean isBlocked();
- method public void setAllowAppOverlay(boolean);
method public void setDescription(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -5746,6 +5744,7 @@ package android.app {
public class NotificationManager {
method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
+ method public boolean areAppOverlaysAllowed();
method public boolean areNotificationsEnabled();
method public boolean canNotifyAsPackage(java.lang.String);
method public void cancel(int);
@@ -10268,6 +10267,7 @@ package android.content {
field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+ field public static final java.lang.String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
field public static final java.lang.String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
@@ -11366,13 +11366,20 @@ package android.content.pm {
method public android.net.Uri getReferrerUri();
method public int getSessionId();
method public long getSize();
+ method public int getStagedSessionErrorCode();
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
+ method public boolean isSessionApplied();
+ method public boolean isSessionFailed();
+ method public boolean isSessionReady();
method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTIVATION_FAILED = 2; // 0x2
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
+ field public static final int NO_ERROR = 0; // 0x0
+ field public static final int VERIFICATION_FAILED = 1; // 0x1
}
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -11565,18 +11572,19 @@ package android.content.pm {
field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
- field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
+ field public static final java.lang.String FEATURE_FACE = "android.hardware.biometrics.face";
field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
- field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
+ field public static final java.lang.String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
- field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
+ field public static final java.lang.String FEATURE_IRIS = "android.hardware.biometrics.iris";
field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -23454,10 +23462,14 @@ package android.media {
public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
+ method public int getAudioSource();
method public int getClientAudioSessionId();
method public int getClientAudioSource();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getClientEffects();
method public android.media.AudioFormat getClientFormat();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getEffects();
method public android.media.AudioFormat getFormat();
+ method public boolean isClientSilenced();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
}
@@ -24987,8 +24999,9 @@ package android.media {
field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
}
- public class MediaMetadataRetriever {
+ public class MediaMetadataRetriever implements java.lang.AutoCloseable {
ctor public MediaMetadataRetriever();
+ method public void close();
method public java.lang.String extractMetadata(int);
method public byte[] getEmbeddedPicture();
method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
@@ -25310,24 +25323,24 @@ package android.media {
method public java.lang.Object clearNextDataSources();
method public void clearPendingCommands();
method public void close();
- method public java.lang.Object deselectTrack(int);
+ method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
method public android.media.AudioAttributes getAudioAttributes();
method public int getAudioSessionId();
- method public long getBufferedPosition();
+ method public long getBufferedPosition(android.media.DataSourceDesc);
method public android.media.DataSourceDesc getCurrentDataSource();
method public long getCurrentPosition();
- method public long getDuration();
+ method public long getDuration(android.media.DataSourceDesc);
method public float getMaxPlayerVolume();
method public android.os.PersistableBundle getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public float getPlayerVolume();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
- method public int getSelectedTrack(int);
+ method public int getSelectedTrack(android.media.DataSourceDesc, int);
method public int getState();
method public android.media.SyncParams getSyncParams();
method public android.media.MediaTimestamp getTimestamp();
- method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+ method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
method public android.media.VideoSize getVideoSize();
method public boolean isLooping();
method public java.lang.Object loopCurrent(boolean);
@@ -25340,7 +25353,7 @@ package android.media {
method public void reset();
method public java.lang.Object seekTo(long);
method public java.lang.Object seekTo(long, int);
- method public java.lang.Object selectTrack(int);
+ method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
method public java.lang.Object setAudioSessionId(int);
method public java.lang.Object setAuxEffectSendLevel(float);
@@ -26075,7 +26088,12 @@ package android.media {
public class ThumbnailUtils {
ctor public ThumbnailUtils();
- method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+ method public static deprecated android.graphics.Bitmap createAudioThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createAudioThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+ method public static deprecated android.graphics.Bitmap createImageThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createImageThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+ method public static deprecated android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createVideoThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int);
method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int, int);
field public static final int OPTIONS_RECYCLE_INPUT = 2; // 0x2
@@ -28318,6 +28336,11 @@ package android.net {
field public int serverAddress;
}
+ public class InetAddresses {
+ method public static boolean isNumericAddress(java.lang.String);
+ method public static java.net.InetAddress parseNumericAddress(java.lang.String);
+ }
+
public final class IpPrefix implements android.os.Parcelable {
method public boolean contains(java.net.InetAddress);
method public int describeContents();
@@ -35128,6 +35151,7 @@ package android.os.storage {
public final class StorageVolume implements android.os.Parcelable {
method public deprecated android.content.Intent createAccessIntent(java.lang.String);
+ method public android.content.Intent createOpenDocumentTreeIntent();
method public int describeContents();
method public java.lang.String getDescription(android.content.Context);
method public java.lang.String getState();
@@ -38336,6 +38360,11 @@ package android.provider {
field public static final java.lang.String VALUE = "value";
}
+ public static final class Settings.Panel {
+ field public static final java.lang.String ACTION_INTERNET_CONNECTIVITY = "android.settings.panel.action.INTERNET_CONNECTIVITY";
+ field public static final java.lang.String ACTION_VOLUME = "android.settings.panel.action.VOLUME";
+ }
+
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
ctor public Settings.Secure();
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
@@ -41224,10 +41253,12 @@ package android.service.quicksettings {
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
method public int getState();
+ method public java.lang.CharSequence getSubtitle();
method public void setContentDescription(java.lang.CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(java.lang.CharSequence);
method public void setState(int);
+ method public void setSubtitle(java.lang.CharSequence);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
@@ -41484,6 +41515,7 @@ package android.service.wallpaper {
method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
+ method public android.content.Context getDisplayContext();
method public android.view.SurfaceHolder getSurfaceHolder();
method public boolean isPreview();
method public boolean isVisible();
@@ -43618,6 +43650,10 @@ package android.telephony {
field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = "opportunistic_network_entry_threshold_rsrp_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final java.lang.String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
@@ -52199,13 +52235,13 @@ package android.view.contentcapture {
method public void setContentCaptureEnabled(boolean);
}
- public final class ContentCaptureSession implements java.lang.AutoCloseable {
+ public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
method public void close();
- method public void destroy();
- method public android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
- method public void notifyViewAppeared(android.view.ViewStructure);
- method public void notifyViewDisappeared(android.view.autofill.AutofillId);
- method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
+ method public final void destroy();
+ method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
+ method public final void notifyViewAppeared(android.view.ViewStructure);
+ method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
+ method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
field public static final int FLAG_USER_INPUT = 1; // 0x1
}
diff --git a/api/system-current.txt b/api/system-current.txt
index a83c32def7be..751ef34f0ae1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27,6 +27,7 @@ package android {
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+ field public static final java.lang.String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
@@ -205,6 +206,10 @@ package android {
field public static final int config_sendPackageName = 17891328; // 0x1110000
}
+ public static final class R.color {
+ field public static final int system_notification_accent_color = 17170460; // 0x106001c
+ }
+
public static final class R.dimen {
field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
@@ -1467,6 +1472,8 @@ package android.hardware.display {
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
+ method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+ method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
method public android.util.Pair<float[], float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1474,10 +1481,22 @@ package android.hardware.display {
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
+ method public int getMaxCorrectionsByCategory();
+ method public int getMaxCorrectionsByPackageName();
method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
}
+ public final class BrightnessCorrection implements android.os.Parcelable {
+ method public float apply(float);
+ method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+ }
+
public final class DisplayManager {
method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -2516,7 +2535,37 @@ package android.hardware.usb {
}
public class UsbManager {
+ method public java.util.List<android.hardware.usb.UsbPort> getPorts();
method public void grantPermission(android.hardware.usb.UsbDevice, java.lang.String);
+ field public static final java.lang.String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+ }
+
+ public final class UsbPort {
+ method public android.hardware.usb.UsbPortStatus getStatus();
+ method public void setRoles(int, int);
+ }
+
+ public final class UsbPortStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCurrentDataRole();
+ method public int getCurrentMode();
+ method public int getCurrentPowerRole();
+ method public int getSupportedRoleCombinations();
+ method public boolean isConnected();
+ method public boolean isRoleCombinationSupported(int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
+ field public static final int DATA_ROLE_DEVICE = 2; // 0x2
+ field public static final int DATA_ROLE_HOST = 1; // 0x1
+ field public static final int DATA_ROLE_NONE = 0; // 0x0
+ field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
+ field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
+ field public static final int MODE_DFP = 2; // 0x2
+ field public static final int MODE_NONE = 0; // 0x0
+ field public static final int MODE_UFP = 1; // 0x1
+ field public static final int POWER_ROLE_NONE = 0; // 0x0
+ field public static final int POWER_ROLE_SINK = 2; // 0x2
+ field public static final int POWER_ROLE_SOURCE = 1; // 0x1
}
}
@@ -3713,6 +3762,7 @@ package android.net.wifi {
method public boolean isWifiScannerSupported();
method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public void setDeviceMobilityState(int);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean startScan(android.os.WorkSource);
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3720,6 +3770,10 @@ package android.net.wifi {
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+ field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
+ field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
+ field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
+ field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -4996,7 +5050,7 @@ package android.service.carrier {
package android.service.contentcapture {
- public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+ public final deprecated class ContentCaptureEventsRequest implements android.os.Parcelable {
method public int describeContents();
method public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents();
method public void writeToParcel(android.os.Parcel, int);
@@ -5008,7 +5062,8 @@ package android.service.contentcapture {
method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
- method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+ method public void onContentCaptureEvent(android.view.contentcapture.ContentCaptureSessionId, android.view.contentcapture.ContentCaptureEvent);
+ method public deprecated void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
@@ -5596,6 +5651,14 @@ package android.telecom {
ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
}
+ public class PhoneAccountSuggestionService extends android.app.Service {
+ ctor public PhoneAccountSuggestionService();
+ method public void onAccountSuggestionRequest(java.lang.String);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+ }
+
public final class RemoteConference {
method public deprecated void setAudioState(android.telecom.AudioState);
}
@@ -5689,6 +5752,83 @@ package android.telephony {
field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
}
+ public class DisconnectCause {
+ field public static final int ALREADY_DIALING = 72; // 0x48
+ field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+ field public static final int BUSY = 4; // 0x4
+ field public static final int CALLING_DISABLED = 74; // 0x4a
+ field public static final int CALL_BARRED = 20; // 0x14
+ field public static final int CALL_PULLED = 51; // 0x33
+ field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+ field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+ field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+ field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+ field public static final int CDMA_DROP = 27; // 0x1b
+ field public static final int CDMA_INTERCEPT = 28; // 0x1c
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+ field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+ field public static final int CDMA_PREEMPTED = 33; // 0x21
+ field public static final int CDMA_REORDER = 29; // 0x1d
+ field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+ field public static final int CDMA_SO_REJECT = 30; // 0x1e
+ field public static final int CONGESTION = 5; // 0x5
+ field public static final int CS_RESTRICTED = 22; // 0x16
+ field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+ field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+ field public static final int DATA_DISABLED = 54; // 0x36
+ field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+ field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+ field public static final int DIALED_MMI = 39; // 0x27
+ field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+ field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+ field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+ field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+ field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+ field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+ field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+ field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+ field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+ field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+ field public static final int FDN_BLOCKED = 21; // 0x15
+ field public static final int ICC_ERROR = 19; // 0x13
+ field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+ field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+ field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+ field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+ field public static final int INCOMING_MISSED = 1; // 0x1
+ field public static final int INCOMING_REJECTED = 16; // 0x10
+ field public static final int INVALID_CREDENTIALS = 10; // 0xa
+ field public static final int INVALID_NUMBER = 7; // 0x7
+ field public static final int LIMIT_EXCEEDED = 15; // 0xf
+ field public static final int LOCAL = 3; // 0x3
+ field public static final int LOST_SIGNAL = 14; // 0xe
+ field public static final int LOW_BATTERY = 61; // 0x3d
+ field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+ field public static final int MMI = 6; // 0x6
+ field public static final int NORMAL = 2; // 0x2
+ field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+ field public static final int NOT_DISCONNECTED = 0; // 0x0
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+ field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+ field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+ field public static final int OUTGOING_CANCELED = 44; // 0x2c
+ field public static final int OUTGOING_FAILURE = 43; // 0x2b
+ field public static final int OUT_OF_NETWORK = 11; // 0xb
+ field public static final int OUT_OF_SERVICE = 18; // 0x12
+ field public static final int POWER_OFF = 17; // 0x11
+ field public static final int SERVER_ERROR = 12; // 0xc
+ field public static final int SERVER_UNREACHABLE = 9; // 0x9
+ field public static final int TIMED_OUT = 13; // 0xd
+ field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+ field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+ field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+ field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+ field public static final int WIFI_LOST = 59; // 0x3b
+ }
+
public class MbmsDownloadSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
}
@@ -5777,14 +5917,134 @@ package android.telephony {
}
public class PhoneStateListener {
+ method public void onCallDisconnectCauseChanged(int, int);
+ method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
+ field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+ field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
}
+ public final class PreciseCallState implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBackgroundCallState();
+ method public int getForegroundCallState();
+ method public int getRingingCallState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
+ field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
+ field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
+ field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
+ field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
+ field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
+ field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
+ field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
+ field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+ field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
+ field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
+ }
+
+ public class PreciseDisconnectCause {
+ field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
+ field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
+ field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
+ field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
+ field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
+ field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
+ field public static final int BUSY = 17; // 0x11
+ field public static final int CALL_BARRED = 240; // 0xf0
+ field public static final int CALL_REJECTED = 21; // 0x15
+ field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+ field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+ field public static final int CDMA_DROP = 1001; // 0x3e9
+ field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+ field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+ field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+ field public static final int CDMA_REORDER = 1003; // 0x3eb
+ field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+ field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+ field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
+ field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
+ field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+ field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int FACILITY_REJECTED = 29; // 0x1d
+ field public static final int FDN_BLOCKED = 241; // 0xf1
+ field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
+ field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
+ field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
+ field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
+ field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+ field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
+ field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
+ field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
+ field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
+ field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
+ field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
+ field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+ field public static final int NETWORK_DETACH = 261; // 0x105
+ field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
+ field public static final int NETWORK_REJECT = 252; // 0xfc
+ field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
+ field public static final int NORMAL = 16; // 0x10
+ field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
+ field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
+ field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
+ field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
+ field public static final int NO_USER_RESPONDING = 18; // 0x12
+ field public static final int NO_VALID_SIM = 249; // 0xf9
+ field public static final int NUMBER_CHANGED = 22; // 0x16
+ field public static final int OEM_CAUSE_1 = 61441; // 0xf001
+ field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
+ field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
+ field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
+ field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
+ field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
+ field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
+ field public static final int OEM_CAUSE_2 = 61442; // 0xf002
+ field public static final int OEM_CAUSE_3 = 61443; // 0xf003
+ field public static final int OEM_CAUSE_4 = 61444; // 0xf004
+ field public static final int OEM_CAUSE_5 = 61445; // 0xf005
+ field public static final int OEM_CAUSE_6 = 61446; // 0xf006
+ field public static final int OEM_CAUSE_7 = 61447; // 0xf007
+ field public static final int OEM_CAUSE_8 = 61448; // 0xf008
+ field public static final int OEM_CAUSE_9 = 61449; // 0xf009
+ field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
+ field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
+ field public static final int OUT_OF_SRV = 248; // 0xf8
+ field public static final int PREEMPTION = 25; // 0x19
+ field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
+ field public static final int QOS_NOT_AVAIL = 49; // 0x31
+ field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
+ field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
+ field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
+ field public static final int RADIO_LINK_LOST = 255; // 0xff
+ field public static final int RADIO_OFF = 247; // 0xf7
+ field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
+ field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
+ field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
+ field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
+ field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
+ field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+ field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+ field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
+ field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+ field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
+ field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
+ field public static final int STATUS_ENQUIRY = 30; // 0x1e
+ field public static final int SWITCHING_CONGESTION = 42; // 0x2a
+ field public static final int TEMPORARY_FAILURE = 41; // 0x29
+ field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
+ field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
+ }
+
public class ServiceState implements android.os.Parcelable {
method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5822,11 +6082,13 @@ package android.telephony {
public class SubscriptionInfo implements android.os.Parcelable {
method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+ method public int getCardId();
}
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method public void requestEmbeddedSubscriptionInfoListRefresh();
+ method public void requestEmbeddedSubscriptionInfoListRefresh(int);
method public void setDefaultDataSubId(int);
method public void setDefaultSmsSubId(int);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
@@ -5889,6 +6151,7 @@ package android.telephony {
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
method public int getPreferredNetworkType(int);
+ method public int getPreferredNetworkTypeBitmap();
method public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
@@ -5917,6 +6180,7 @@ package android.telephony {
method public void setDataActivationState(int);
method public deprecated void setDataEnabled(int, boolean);
method public void setDataRoamingEnabled(boolean);
+ method public boolean setPreferredNetworkTypeBitmap(int);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public void setSimPowerState(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index d534501e1a06..71a06f11b692 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -182,7 +182,6 @@ package android.app {
method public int getUserLockedFields();
method public void lockFields(int);
method public void setBlocked(boolean);
- field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2
}
public class NotificationManager {
@@ -639,6 +638,11 @@ package android.media {
method public static boolean isEncodingLinearPcm(int);
}
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]);
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String);
+ }
+
public final class BufferingParams implements android.os.Parcelable {
method public int describeContents();
method public int getInitialMarkMs();
@@ -655,11 +659,6 @@ package android.media {
method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
}
- public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
- method public android.media.BufferingParams getBufferingParams();
- method public void setBufferingParams(android.media.BufferingParams);
- }
-
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1287,6 +1286,14 @@ package android.telecom {
ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
}
+ public class PhoneAccountSuggestionService extends android.app.Service {
+ ctor public PhoneAccountSuggestionService();
+ method public void onAccountSuggestionRequest(java.lang.String);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+ }
+
}
package android.telephony {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index e3748f1653ab..3defdc5467c8 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -247,7 +247,7 @@ public class Bmgr {
}
try {
- mBmgr.dataChanged(pkg);
+ mBmgr.dataChangedForUser(userId, pkg);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -264,7 +264,8 @@ public class Bmgr {
}
if (allPkgs.size() > 0) {
try {
- mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
+ mBmgr.fullTransportBackupForUser(
+ userId, allPkgs.toArray(new String[allPkgs.size()]));
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -393,7 +394,7 @@ public class Bmgr {
installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
String[] filteredPackages = {};
try {
- filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
+ filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -498,11 +499,11 @@ public class Bmgr {
}
if ("-c".equals(which)) {
- doTransportByComponent();
+ doTransportByComponent(userId);
return;
}
- String old = mBmgr.selectBackupTransport(which);
+ String old = mBmgr.selectBackupTransportForUser(userId, which);
if (old == null) {
System.out.println("Unknown transport '" + which
+ "' specified; no changes made.");
@@ -516,7 +517,7 @@ public class Bmgr {
}
}
- private void doTransportByComponent() {
+ private void doTransportByComponent(@UserIdInt int userId) {
String which = nextArg();
if (which == null) {
showUsage();
@@ -526,7 +527,9 @@ public class Bmgr {
final CountDownLatch latch = new CountDownLatch(1);
try {
- mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+ mBmgr.selectBackupTransportAsyncForUser(
+ userId,
+ ComponentName.unflattenFromString(which),
new ISelectBackupTransportCallback.Stub() {
@Override
public void onSuccess(String transportName) {
@@ -567,7 +570,7 @@ public class Bmgr {
}
try {
- mBmgr.clearBackupData(transport, pkg);
+ mBmgr.clearBackupDataForUser(userId, transport, pkg);
System.out.println("Wiped backup data for " + pkg + " on " + transport);
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -599,7 +602,8 @@ public class Bmgr {
InitObserver observer = new InitObserver();
try {
System.out.println("Initializing transports: " + transports);
- mBmgr.initializeTransports(transports.toArray(new String[transports.size()]), observer);
+ mBmgr.initializeTransportsForUser(
+ userId, transports.toArray(new String[transports.size()]), observer);
observer.waitForCompletion(30*1000L);
System.out.println("Initialization result: " + observer.result);
} catch (RemoteException e) {
@@ -611,13 +615,13 @@ public class Bmgr {
private void doList(@UserIdInt int userId) {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
- doListTransports();
+ doListTransports(userId);
return;
}
// The rest of the 'list' options work with a restore session on the current transport
try {
- mRestore = mBmgr.beginRestoreSession(null, null);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
@@ -634,19 +638,19 @@ public class Bmgr {
}
}
- private void doListTransports() {
+ private void doListTransports(@UserIdInt int userId) {
String arg = nextArg();
try {
if ("-c".equals(arg)) {
- for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+ for (ComponentName transport : mBmgr.listAllTransportComponentsForUser(userId)) {
System.out.println(transport.flattenToShortString());
}
return;
}
- String current = mBmgr.getCurrentTransport();
- String[] transports = mBmgr.listAllTransports();
+ String current = mBmgr.getCurrentTransportForUser(userId);
+ String[] transports = mBmgr.listAllTransportsForUser(userId);
if (transports == null || transports.length == 0) {
System.out.println("No transports available.");
return;
@@ -756,7 +760,7 @@ public class Bmgr {
filter.add(arg);
}
- doRestoreAll(token, filter);
+ doRestoreAll(userId, token, filter);
} catch (NumberFormatException e) {
showUsage();
return;
@@ -769,12 +773,12 @@ public class Bmgr {
System.err.println("'restore <token> <package>'.");
}
- private void doRestoreAll(long token, HashSet<String> filter) {
+ private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
RestoreObserver observer = new RestoreObserver();
try {
boolean didRestore = false;
- mRestore = mBmgr.beginRestoreSession(null, null);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 020c443582b2..8d0cee5b938d 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -36,6 +36,7 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"
@@ -53,39 +54,40 @@ using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
+using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace {
-std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
- const std::string& res,
- const std::string& fallback_package) {
+
+Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
+ const std::string& fallback_package) {
// first, try to parse as a hex number
char* endptr = nullptr;
ResourceId resid;
resid = strtol(res.c_str(), &endptr, 16);
if (*endptr == '\0') {
- return std::make_pair(true, resid);
+ return {resid};
}
// next, try to parse as a package:type/name string
resid = am.GetResourceId(res, "", fallback_package);
if (is_valid_resid(resid)) {
- return std::make_pair(true, resid);
+ return {resid};
}
// end of the road: res could not be parsed
- return std::make_pair(false, 0);
+ return {};
}
-std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
Res_value value;
ResTable_config config;
uint32_t flags;
ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
if (cookie == kInvalidCookie) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
@@ -123,31 +125,31 @@ std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, Resou
out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
break;
}
- return std::make_pair(true, out);
+ return {out};
}
-std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
const auto zip = ZipFile::Open(apk_path);
if (!zip) {
- return std::make_pair(false, "");
+ return {};
}
const auto entry = zip->Uncompress("AndroidManifest.xml");
if (!entry) {
- return std::make_pair(false, "");
+ return {};
}
const auto xml = Xml::Create(entry->buf, entry->size);
if (!xml) {
- return std::make_pair(false, "");
+ return {};
}
const auto tag = xml->FindTag("overlay");
if (!tag) {
- return std::make_pair(false, "");
+ return {};
}
const auto iter = tag->find("targetPackage");
if (iter == tag->end()) {
- return std::make_pair(false, "");
+ return {};
}
- return std::make_pair(true, iter->second);
+ return {iter->second};
}
} // namespace
@@ -195,14 +197,14 @@ bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
}
apk_assets.push_back(std::move(target_apk));
- bool lookup_ok;
- std::tie(lookup_ok, target_package_name) =
+ const Result<std::string> package_name =
GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!lookup_ok) {
+ if (!package_name) {
out_error << "error: failed to parse android:targetPackage from overlay manifest"
<< std::endl;
return false;
}
+ target_package_name = *package_name;
} else if (target_path != idmap_header->GetTargetPath()) {
out_error << "error: different target APKs (expected target APK " << target_path << " but "
<< idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
@@ -227,21 +229,18 @@ bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
am.SetApkAssets(raw_pointer_apk_assets);
am.SetConfiguration(config);
- ResourceId resid;
- bool lookup_ok;
- std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
- if (!lookup_ok) {
+ const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+ if (!resid) {
out_error << "error: failed to parse resource ID" << std::endl;
return false;
}
- std::string value;
- std::tie(lookup_ok, value) = GetValue(am, resid);
- if (!lookup_ok) {
- out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+ const Result<std::string> value = GetValue(am, *resid);
+ if (!value) {
+ out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
return false;
}
- std::cout << value << std::endl;
+ std::cout << *value << std::endl;
return true;
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index cf72cb94da2c..86b00f1d6e95 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -78,6 +78,18 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
}
}
+Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+ int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+ assert(_aidl_return);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ std::stringstream dev_null;
+ *_aidl_return = header && header->IsUpToDate(dev_null);
+ return ok();
+}
+
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return) {
@@ -90,17 +102,6 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
_aidl_return->reset(nullptr);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
- std::ifstream fin(idmap_path);
- const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
- fin.close();
- // do not reuse error stream from IsUpToDate below, or error messages will be
- // polluted with irrelevant data
- std::stringstream dev_null;
- if (header && header->IsUpToDate(dev_null)) {
- return ok();
- }
-
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
@@ -119,6 +120,7 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
}
umask(0133); // u=rw,g=r,o=r
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 2b32042d6aa3..4e5abc9069f4 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -39,6 +39,9 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
bool* _aidl_return);
+ binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
+ bool* _aidl_return);
+
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return);
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index 5d196101a7a6..d475417a0935 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -22,6 +22,7 @@ package android.os;
interface IIdmap2 {
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+ boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath, int userId);
}
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 88a835b6439c..d106f19af998 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -18,19 +18,18 @@
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid);
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
} // namespace utils
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
new file mode 100644
index 000000000000..6189ea371ee1
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+
+#include <optional>
+
+namespace android::idmap2 {
+
+template <typename T>
+using Result = std::optional<T>;
+
+static constexpr std::nullopt_t kResultError = std::nullopt;
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 328bd367adfc..9edbbe0cce90 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -19,10 +19,10 @@
#include <memory>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "ziparchive/zip_archive.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -43,7 +43,7 @@ class ZipFile {
static std::unique_ptr<const ZipFile> Open(const std::string& path);
std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
- std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+ Result<uint32_t> Crc(const std::string& entryPath) const;
~ZipFile();
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 5a47e301b66c..1ef326793cb4 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -33,6 +33,7 @@
#include "idmap2/Idmap.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -143,18 +144,16 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
return false;
}
- bool status;
- uint32_t target_crc;
- std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+ if (!target_crc) {
out_error << "error: failed to get target crc" << std::endl;
return false;
}
- if (target_crc_ != target_crc) {
+ if (target_crc_ != *target_crc) {
out_error << base::StringPrintf(
"error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
- target_crc_, target_crc)
+ target_crc_, *target_crc)
<< std::endl;
return false;
}
@@ -165,17 +164,16 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
return false;
}
- uint32_t overlay_crc;
- std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+ if (!overlay_crc) {
out_error << "error: failed to get overlay crc" << std::endl;
return false;
}
- if (overlay_crc_ != overlay_crc) {
+ if (overlay_crc_ != *overlay_crc) {
out_error << base::StringPrintf(
"error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
- overlay_crc_, overlay_crc)
+ overlay_crc_, *overlay_crc)
<< std::endl;
return false;
}
@@ -322,17 +320,20 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- bool crc_status;
- std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
- if (!crc_status) {
+
+ Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for target" << std::endl;
return nullptr;
}
- std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
- if (!crc_status) {
+ header->target_crc_ = *crc;
+
+ crc = overlay_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for overlay" << std::endl;
return nullptr;
}
+ header->overlay_crc_ = *crc;
if (target_apk_path.size() > sizeof(header->target_path_)) {
out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
@@ -358,15 +359,14 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_
const auto end = overlay_pkg->end();
for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
const ResourceId overlay_resid = *iter;
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!lookup_ok) {
+ Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+ if (!name) {
continue;
}
// prepend "<package>:" to turn name into "<package>:<type>/<name>"
- name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
- const ResourceId target_resid = NameToResid(target_asset_manager, name);
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
+ const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
if (target_resid == 0) {
continue;
}
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 492e6f049d68..fb3bc5ba9ae8 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -15,7 +15,6 @@
*/
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -23,6 +22,7 @@
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -63,11 +63,9 @@ void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) {
stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
- if (lookup_ok) {
- stream_ << " " << name;
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ if (name) {
+ stream_ << " " << *name;
}
}
stream_ << std::endl;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 57cfc8ef85b4..7c24445ef902 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,7 +16,6 @@
#include <cstdarg>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -24,6 +23,7 @@
#include "idmap2/RawPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::ApkAssets;
@@ -75,14 +75,13 @@ void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) {
const ResourceId target_resid =
RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
- bool lookup_ok = false;
- std::string name;
+ Result<std::string> name;
if (target_package_loaded) {
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+ name = utils::ResToTypeEntryName(target_am_, target_resid);
}
- if (lookup_ok) {
+ if (name) {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name.c_str());
+ name->c_str());
} else {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
}
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e98f843931c8..5c897832e9d5 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -15,12 +15,12 @@
*/
#include <string>
-#include <utility>
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::StringPiece16;
using android::util::Utf16ToUtf8;
@@ -29,11 +29,10 @@ namespace android {
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid) {
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
if (name.type != nullptr) {
@@ -47,7 +46,7 @@ std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2&
} else {
out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
}
- return std::make_pair(true, out);
+ return {out};
}
} // namespace utils
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 3f2079a380d6..9fb611dd8e8d 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -16,8 +16,8 @@
#include <memory>
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -57,10 +57,10 @@ std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryP
return chunk;
}
-std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
::ZipEntry entry;
int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
- return std::make_pair(status == 0, entry.crc32);
+ return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 0547fa00de3d..7f60d7529a61 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -16,13 +16,13 @@
#include <memory>
#include <string>
-#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "androidfw/ApkAssets.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "TestHelpers.h"
@@ -52,17 +52,14 @@ class ResourceUtilsTests : public Idmap2Tests {
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
- ASSERT_TRUE(lookup_ok);
- ASSERT_EQ(name, "integer/int1");
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ ASSERT_TRUE(name);
+ ASSERT_EQ(*name, "integer/int1");
}
TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
- bool lookup_ok;
- std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
- ASSERT_FALSE(lookup_ok);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ ASSERT_FALSE(name);
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index a504d3126c05..6e4a501a51ac 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -16,8 +16,8 @@
#include <cstdio> // fclose
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
#include "gmock/gmock.h"
@@ -44,14 +44,12 @@ TEST(ZipFileTests, Crc) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
- bool status;
- uint32_t crc;
- std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
- ASSERT_TRUE(status);
- ASSERT_EQ(crc, 0x762f3d24);
+ Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
+ ASSERT_TRUE(crc);
+ ASSERT_EQ(*crc, 0x762f3d24);
- std::tie(status, std::ignore) = zip->Crc("does-not-exist");
- ASSERT_FALSE(status);
+ Result<uint32_t> crc2 = zip->Crc("does-not-exist");
+ ASSERT_FALSE(crc2);
}
TEST(ZipFileTests, Uncompress) {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a9819972cfc7..69cb264ef237 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -293,7 +293,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTim
// Then, check stats-data directory to see there's any file containing
// ConfigMetricsReport from previous shutdowns to concatenate to reports.
- StorageManager::appendConfigMetricsReport(key, proto);
+ StorageManager::appendConfigMetricsReport(key, proto, erase_data);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 04173b217dcb..50b64b986866 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -296,6 +296,7 @@ void StatsService::dumpIncidentSection(int out) {
ADB_DUMP, &proto);
proto.end(reportsListToken);
proto.flush(out);
+ proto.clear();
}
}
@@ -466,23 +467,12 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[1].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[2].c_str(), args[2].size());
- good = true;
- }
- }
- } else {
- dprintf(out,
- "The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
+ dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[2].c_str(), args[2].size());
}
if (!good) {
print_cmd_help(out);
@@ -518,23 +508,12 @@ status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& arg
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[2].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[3].c_str(), args[3].size());
- good = true;
- }
- }
- } else {
- dprintf(err,
- "The config can only be set for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 2, uid);
+ if (!good) {
+ dprintf(err, "Invalid UID. Note that the config can only be set for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[3].c_str(), args[3].size());
} else if (argCount == 2 && args[1] == "remove") {
good = true;
}
@@ -612,23 +591,12 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[1].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[2].c_str(), args[2].size());
- good = true;
- }
- }
- } else {
- dprintf(out,
- "The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
+ dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[2].c_str(), args[2].size());
}
if (good) {
vector<uint8_t> data;
@@ -714,18 +682,14 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar
state = atoi(args[2].c_str());
good = true;
} else if (argCount == 4) {
- uid = atoi(args[1].c_str());
- // If it's a userdebug or eng build, then the shell user can impersonate other uids.
- // Otherwise, the uid must match the actual caller's uid.
- if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
- label = atoi(args[2].c_str());
- state = atoi(args[3].c_str());
- good = true;
- } else {
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
dprintf(out,
- "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
- "on eng or userdebug builds.\n");
+ "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be "
+ "done for other UIDs on eng or userdebug builds.\n");
}
+ label = atoi(args[2].c_str());
+ state = atoi(args[3].c_str());
}
if (good) {
dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
@@ -792,6 +756,28 @@ status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
}
}
+bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
+ const char* s = args[uidArgIndex].c_str();
+ if (*s == '\0') {
+ return false;
+ }
+ char* endc = NULL;
+ int64_t longUid = strtol(s, &endc, 0);
+ if (*endc != '\0') {
+ return false;
+ }
+ int32_t goodUid = static_cast<int32_t>(longUid);
+ if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) {
+ return false; // It was not of uid_t type.
+ }
+ uid = goodUid;
+
+ int32_t callingUid = IPCThreadState::self()->getCallingUid();
+ return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
+ || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
+ || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& version_string,
const vector<String16>& app,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cd4d601a606f..135a3c9cde51 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -291,6 +291,15 @@ private:
status_t cmd_print_logs(int outFd, const Vector<String8>& args);
/**
+ * Writes the value of args[uidArgIndex] into uid.
+ * Returns whether the uid is reasonable (type uid_t) and whether
+ * 1. it is equal to the calling uid, or
+ * 2. the device is mEngBuild, or
+ * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
+ */
+ bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
+
+ /**
* Adds a configuration after checking permissions and obtaining UID from binder call.
*/
bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
@@ -340,6 +349,7 @@ private:
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
+ FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5899e0cfbca1..fd62843701b2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -171,6 +171,8 @@ message Atom {
DocsUIUserActionReported docs_ui_user_action_reported = 112;
WifiEnabledStateChanged wifi_enabled_state_changed = 113;
WifiRunningStateChanged wifi_running_state_changed = 114;
+ AppCompacted app_compacted = 115;
+ NetworkDnsEventReported network_dns_event_Reported = 116;
}
// Pulled events will start at field 10000.
@@ -3649,3 +3651,113 @@ message DocsUIUserActionReported {
message DocsUIInvalidScopedAccessRequestReported {
optional android.stats.docsui.InvalidScopedAccess type = 1;
}
+
+/**
+ * Logs when an app's memory is compacted.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppCompacted {
+ // The pid of the process being compacted.
+ optional int32 pid = 1;
+
+ // The name of the process being compacted.
+ optional string process_name = 2;
+
+ // The type of compaction.
+ enum Action {
+ UNKNOWN = 0;
+ SOME = 1;
+ FULL = 2;
+ }
+ optional Action action = 3;
+
+ // Total RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_total_kilobytes = 4;
+
+ // File RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_file_kilobytes = 5;
+
+ // Anonymous RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_anon_kilobytes = 6;
+
+ // Swap in kilobytes consumed by the process prior to compaction.
+ optional int64 before_swap_kilobytes = 7;
+
+ // Total RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_total_kilobytes = 8;
+
+ // File RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_file_kilobytes = 9;
+
+ // Anonymous RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_anon_kilobytes = 10;
+
+ // Swap in kilobytes consumed by the process after compaction.
+ optional int64 after_swap_kilobytes = 11;
+
+ // The time taken to perform compaction in milliseconds.
+ optional int64 time_to_compact_millis = 12;
+
+ // The last compaction action performed for this app.
+ optional Action last_action = 13;
+
+ // The last time that compaction was attempted on this process in milliseconds
+ // since boot, not including sleep (see SystemClock.uptimeMillis()).
+ optional int64 last_compact_timestamp_ms_since_boot = 14;
+
+ // The oom_score_adj at the time of compaction.
+ optional int32 oom_score_adj = 15;
+
+ // The process state at the time of compaction.
+ optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
+}
+
+/**
+ * Logs the latency period(in microseconds) and the return code of
+ * the DNS(Domain Name System) lookups.
+ * These 4 methods(GETADDRINFO,GETHOSTBYNAME,GETHOSTBYADDR,RES_NSEND)
+ * to get info(address or hostname) from DNS server(or DNS cache).
+ * Logged from:
+ * /system/netd/server/DnsProxyListener.cpp
+ */
+message NetworkDnsEventReported {
+ // The types of the DNS lookups, as defined in
+ //system/netd/server/binder/android/net/metrics/INetdEventListener.aidl
+ enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+ }
+ optional EventType event_type = 1;
+
+ // The return value of the DNS resolver for each DNS lookups.
+ //bionic/libc/include/netdb.h
+ //system/netd/resolv/include/netd_resolv/resolv.h
+ enum ReturnCode {
+ EAI_NOERR = 0;
+ EAI_ADDRFAMILY = 1;
+ EAI_AGAIN = 2;
+ EAI_BADFLAGS = 3;
+ EAI_FAIL = 4;
+ EAI_FAMILY = 5;
+ EAI_MEMORY = 6;
+ EAI_NODATA = 7;
+ EAI_NONAME = 8;
+ EAI_SERVICE = 9;
+ EAI_SOCKTYPE = 10;
+ EAI_SYSTEM = 11;
+ EAI_BADHINTS = 12;
+ EAI_PROTOCOL = 13;
+ EAI_OVERFLOW = 14;
+ RESOLV_TIMEOUT = 255;
+ EAI_MAX = 256;
+ }
+ optional ReturnCode return_code = 2;
+
+ // The latency period(in microseconds) it took for this DNS lookup to complete.
+ optional int32 latency_micros = 3;
+}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 13579d283de0..7cc57c12063c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -154,7 +154,7 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 0425671b6367..69bafc354643 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -463,7 +463,7 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
VLOG(" Duration metric, empty return");
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ea125d02f337..c53c4ce5e0d6 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -110,7 +110,7 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mProto->size() <= 0) {
return;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 98a33f5280e6..ec602445d0ed 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -194,7 +194,7 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
return;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7475b53c0a84..cf56e2d43385 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -192,7 +192,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 2f19a02ecafe..90f641a34b85 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -188,7 +188,9 @@ bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
return false;
}
-void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
+ ProtoOutputStream* proto,
+ bool erasa_data) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", STATS_DATA_DIR);
@@ -224,8 +226,9 @@ void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutput
close(fd);
}
- // Remove file from disk after reading.
- remove(file_name.c_str());
+ if (erasa_data) {
+ remove(file_name.c_str());
+ }
}
}
}
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 8fbc89e4f657..dcf3bb607380 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -68,10 +68,12 @@ public:
static bool hasConfigMetricsReport(const ConfigKey& key);
/**
- * Appends ConfigMetricsReport found on disk to the specific proto and
- * delete it.
+ * Appends the ConfigMetricsReport found on disk to the specifid proto
+ * and, if erase_data, deletes it from disk.
*/
- static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto);
+ static void appendConfigMetricsReport(const ConfigKey& key,
+ ProtoOutputStream* proto,
+ bool erase_data);
/**
* Call to load the saved configs from disk.
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 237f8b902015..d52be441f6b6 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -279,7 +279,10 @@ TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
// Dump report again. There should be no data since we erased it.
processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
output.ParseFromArray(bytes.data(), bytes.size());
- bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+ // We don't care whether statsd has a report, as long as it has no count metrics in it.
+ bool noData = output.reports_size() == 0
+ || output.reports(0).metrics_size() == 0
+ || output.reports(0).metrics(0).count_metrics().data_size() == 0;
EXPECT_TRUE(noData);
}
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index a7b413666266..560fb9f02174 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -58,6 +58,45 @@ TEST(StatsServiceTest, TestAddConfig_invalid) {
service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
+TEST(StatsServiceTest, TestGetUidFromArgs) {
+ Vector<String8> args;
+ args.push(String8("-1"));
+ args.push(String8("0"));
+ args.push(String8("1"));
+ args.push(String8("9999999999999999999999999999999999"));
+ args.push(String8("a1"));
+ args.push(String8(""));
+
+ int32_t uid;
+
+ StatsService service(nullptr);
+ service.mEngBuild = true;
+
+ // "-1"
+ EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+
+ // "0"
+ EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+ EXPECT_EQ(0, uid);
+
+ // "1"
+ EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+ EXPECT_EQ(1, uid);
+
+ // "999999999999999999"
+ EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+
+ // "a1"
+ EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+
+ // ""
+ EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+
+ // For a non-userdebug, uid "1" cannot be impersonated.
+ service.mEngBuild = false;
+ EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 8464b8df03d0..91d6490fe5a1 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -62,7 +62,7 @@ public class Utils {
if (process.waitFor() == 0) {
logger.fine("Adb command successful.");
} else {
- logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+ logger.severe("Abnormal adb shell termination for: " + String.join(",", commands));
throw new RuntimeException("Error running adb command: " + err.toString());
}
}
@@ -118,6 +118,52 @@ public class Utils {
logger.addHandler(handler);
}
+ /**
+ * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is
+ * minCodename or higher.
+ * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
+ * If all else fails, assume it will work (letting future commands deal with any errors).
+ */
+ public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+ BufferedReader in = null;
+ try {
+ File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
+ outFileSdk.deleteOnExit();
+ runCommand(outFileSdk, logger,
+ "adb", "shell", "getprop", "ro.build.version.sdk");
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
+ // If NullPointerException/NumberFormatException/etc., just catch and return true.
+ int sdk = Integer.parseInt(in.readLine().trim());
+ if (sdk >= minSdk) {
+ return true;
+ } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development.
+ in.close();
+ File outFileCode = File.createTempFile("shelltools_codename", "tmp");
+ outFileCode.deleteOnExit();
+ runCommand(outFileCode, logger,
+ "adb", "shell", "getprop", "ro.build.version.codename");
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
+ return in.readLine().startsWith(minCodename);
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ logger.fine("Could not determine whether statsd version is compatibile "
+ + "with tool: " + e.toString());
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e) {
+ logger.fine("Could not close temporary file: " + e.toString());
+ }
+ }
+ // Could not determine whether statsd is acceptable version.
+ // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them.
+ return true;
+ }
+
public static class LocalToolsFormatter extends Formatter {
public String format(LogRecord record) {
return record.getMessage() + "\n";
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 67fb906c2570..2eb46605b28d 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -37,6 +37,9 @@ import java.util.logging.Logger;
public class LocalDrive {
private static final boolean DEBUG = false;
+ public static final int MIN_SDK = 29;
+ public static final String MIN_CODENAME = "Q";
+
public static final long DEFAULT_CONFIG_ID = 56789;
public static final String BINARY_FLAG = "--binary";
@@ -46,7 +49,7 @@ public class LocalDrive {
public static final String HELP_STRING =
"Usage:\n\n" +
- "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Uploads the given statsd config file (in binary or human-readable-text format).\n" +
" If a config with this id already exists, removes it first.\n" +
" CONFIG_FILE Location of config file on host.\n" +
@@ -56,12 +59,12 @@ public class LocalDrive {
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Same as upload, but does not remove the old config first (if it already exists).\n" +
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
" Prints the output statslog data (in binary or human-readable-text format).\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
" --binary Output should be in binary, instead of default human-readable text.\n" +
@@ -72,13 +75,13 @@ public class LocalDrive {
// --include_current_bucket --proto
"\n" +
- "statsd_local remove [CONFIG_ID]\n" +
+ "statsd_localdrive remove [CONFIG_ID]\n" +
" Removes the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
"\n" +
- "statsd_local clear [CONFIG_ID]\n" +
+ "statsd_localdrive clear [CONFIG_ID]\n" +
" Clears the data associated with the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -92,6 +95,12 @@ public class LocalDrive {
public static void main(String[] args) {
Utils.setUpLogger(sLogger, DEBUG);
+ if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+ sLogger.severe("LocalDrive only works with statsd versions for Android "
+ + MIN_CODENAME + " or higher.");
+ return;
+ }
+
if (args.length > 0) {
switch (args[0]) {
case "clear":
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index e3fe928a1309..02de7f05be7c 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -194,6 +194,7 @@ public class TestDrive {
+ "allowed_log_source: \"AID_SYSTEM\"\n"
+ "allowed_log_source: \"AID_ROOT\"\n"
+ "allowed_log_source: \"AID_BLUETOOTH\"\n"
+ + "allowed_log_source: \"AID_LMKD\"\n"
+ "\n"
+ "hash_strings_in_metric_report: false";
@@ -216,6 +217,7 @@ public class TestDrive {
+ "allowed_log_source: \"AID_SYSTEM\"\n"
+ "allowed_log_source: \"AID_ROOT\"\n"
+ "allowed_log_source: \"AID_BLUETOOTH\"\n"
+ + "allowed_log_source: \"AID_LMKD\"\n"
+ "\n"
+ "hash_strings_in_metric_report: false";
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a39f5e30bd51..4174ad7cd586 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -51,6 +51,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
+ "set-phone-acct-suggestion-component";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,36 +66,37 @@ public final class Telecom extends BaseCommand {
@Override
public void onShowUsage(PrintStream out) {
- out.println(
- "usage: telecom [subcommand] [options]\n" +
- "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
- "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
- "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
- "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
- "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
- "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
- "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom set-default-dialer <PACKAGE>\n" +
- "usage: telecom get-default-dialer\n" +
- "usage: telecom get-system-dialer\n" +
- "usage: telecom wait-on-handlers\n" +
- "\n" +
- "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" +
- " already been registered with Telecom.\n" +
- "\n" +
- "telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
- " has already been registered with telecom.\n" +
- "\n" +
- "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
- "\n" +
- "telecom get-default-dialer: Displays the current default dialer. \n" +
- "\n" +
- "telecom get-system-dialer: Displays the current system dialer. \n" +
- "\n" +
- "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
- );
+ out.println("usage: telecom [subcommand] [options]\n"
+ + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+ + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+ + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
+ + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
+ + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
+ + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
+ + " <LABEL> <ADDRESS>\n"
+ + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom set-default-dialer <PACKAGE>\n"
+ + "usage: telecom get-default-dialer\n"
+ + "usage: telecom get-system-dialer\n"
+ + "usage: telecom wait-on-handlers\n"
+ + "\n"
+ + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n"
+ + " already been registered with Telecom.\n"
+ + "\n"
+ + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
+ + " has already been registered with telecom.\n"
+ + "\n"
+ + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+ + "\n"
+ + "telecom get-default-dialer: Displays the current default dialer. \n"
+ + "\n"
+ + "telecom get-system-dialer: Displays the current system dialer. \n"
+ + "\n"
+ + "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
+ );
}
@Override
@@ -134,6 +137,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_SET_TEST_AUTO_MODE_APP:
runSetTestAutoModeApp();
break;
+ case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
+ runSetTestPhoneAcctSuggestionComponent();
+ break;
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
@@ -216,6 +222,11 @@ public final class Telecom extends BaseCommand {
mTelecomService.setTestAutoModeApp(packageName);
}
+ private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
+ final String componentName = nextArg();
+ mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b45d172cb89..497e193c38ec 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6152,7 +6152,7 @@ public final class ActivityThread extends ClientTransactionHandler {
try {
synchronized (getGetProviderLock(auth, userId)) {
holder = ActivityManager.getService().getContentProvider(
- getApplicationThread(), auth, userId, stable);
+ getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6905cb5cea73..791f3da0d5e8 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -41,8 +41,10 @@ import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.Preconditions;
@@ -86,10 +88,20 @@ public class AppOpsManager {
*/
final Context mContext;
+
@UnsupportedAppUsage
final IAppOpsService mService;
- final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
- final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+
+ @GuardedBy("mModeWatchers")
+ private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
+ new ArrayMap<>();
+
+ @GuardedBy("mActiveWatchers")
+ private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+ new ArrayMap<>();
+
+ @GuardedBy("mNotedWatchers")
+ private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
new ArrayMap<>();
static IBinder sToken;
@@ -2471,6 +2483,23 @@ public class AppOpsManager {
}
/**
+ * Callback for notification of an op being noted.
+ *
+ * @hide
+ */
+ public interface OnOpNotedListener {
+ /**
+ * Called when an op was noted.
+ *
+ * @param code The op code.
+ * @param uid The UID performing the operation.
+ * @param packageName The package performing the operation.
+ * @param result The result of the note.
+ */
+ void onOpNoted(String code, int uid, String packageName, int result);
+ }
+
+ /**
* Callback for notification of changes to operation state.
* This allows you to see the raw op codes instead of strings.
* @hide
@@ -2819,7 +2848,7 @@ public class AppOpsManager {
*/
public void stopWatchingMode(OnOpChangedListener callback) {
synchronized (mModeWatchers) {
- IAppOpsCallback cb = mModeWatchers.get(callback);
+ IAppOpsCallback cb = mModeWatchers.remove(callback);
if (cb != null) {
try {
mService.stopWatchingMode(cb);
@@ -2893,7 +2922,7 @@ public class AppOpsManager {
@TestApi
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
synchronized (mActiveWatchers) {
- final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+ final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
if (cb != null) {
try {
mService.stopWatchingActive(cb);
@@ -2904,6 +2933,74 @@ public class AppOpsManager {
}
}
+ /**
+ * Start watching for noted app ops. An app op may be immediate or long running.
+ * Immediate ops are noted while long running ones are started and stopped. This
+ * method allows registering a listener to be notified when an app op is noted. If
+ * an op is being noted by any package you will get a callback. To change the
+ * watched ops for a registered callback you need to unregister and register it again.
+ *
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can watch changes only for your UID.
+ *
+ * @param ops The ops to watch.
+ * @param callback Where to report changes.
+ *
+ * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+ * @see #stopWatchingNoted(OnOpNotedListener)
+ * @see #noteOp(String, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+ IAppOpsNotedCallback cb;
+ synchronized (mNotedWatchers) {
+ cb = mNotedWatchers.get(callback);
+ if (cb != null) {
+ return;
+ }
+ cb = new IAppOpsNotedCallback.Stub() {
+ @Override
+ public void opNoted(int op, int uid, String packageName, int mode) {
+ callback.onOpNoted(sOpToString[op], uid, packageName, mode);
+ }
+ };
+ mNotedWatchers.put(callback, cb);
+ }
+ try {
+ final int[] opCodes = new int[ops.length];
+ for (int i = 0; i < opCodes.length; i++) {
+ opCodes[i] = strOpToOp(ops[i]);
+ }
+ mService.startWatchingNoted(opCodes, cb);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop watching for noted app ops. An app op may be immediate or long running.
+ * Unregistering a non-registered callback has no effect.
+ *
+ * @see #startWatchingNoted(String[], OnOpNotedListener)
+ * @see #noteOp(String, int, String)
+ *
+ * @hide
+ */
+ public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
+ synchronized (mNotedWatchers) {
+ final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
+ if (cb != null) {
+ try {
+ mService.stopWatchingNoted(cb);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
}
@@ -2981,7 +3078,7 @@ public class AppOpsManager {
*/
public int unsafeCheckOpRaw(String op, int uid, String packageName) {
try {
- return mService.checkOperation(strOpToOp(op), uid, packageName);
+ return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 7fe21b23738c..139a39fe3a1d 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -37,10 +37,11 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which to check.
* @param packageName The package for which to check.
* @param superImpl The super implementation.
+ * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
* @return The app op check result.
*/
- int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl);
+ int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
/**
* Allows overriding check audio operation behavior.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 88fb025bf406..fb519b625012 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -124,7 +124,7 @@ interface IActivityManager {
int ignoreWindowingMode);
void moveTaskToFront(int task, int flags, in Bundle options);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
- ContentProviderHolder getContentProvider(in IApplicationThread caller,
+ ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,
in String name, int userId, boolean stable);
void publishContentProviders(in IApplicationThread caller,
in List<ContentProviderHolder> providers);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 00567523e393..163be8efc8fd 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,6 +65,10 @@ interface INotificationManager
boolean areNotificationsEnabled(String pkg);
int getPackageImportance(String pkg);
+ void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
+ boolean areAppOverlaysAllowed(String pkg);
+ boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+
void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b9d590741263..e066f06542c3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3130,6 +3130,10 @@ public class Notification implements Parcelable
return mAppOverlayIntent;
}
+ /**
+ * Returns whether the platform is allowed (by the app developer) to generate contextual actions
+ * for this notification.
+ */
public boolean getAllowSystemGeneratedContextualActions() {
return mAllowSystemGeneratedContextualActions;
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 2322a42c93d5..34cd9f029746 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -50,20 +50,12 @@ public final class NotificationChannelGroup implements Parcelable {
private static final String ATT_DESC = "desc";
private static final String ATT_ID = "id";
private static final String ATT_BLOCKED = "blocked";
- private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
private static final String ATT_USER_LOCKED = "locked";
- private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
-
/**
* @hide
*/
public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
- /**
- * @hide
- */
- @TestApi
- public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002;
/**
* @see #getId()
@@ -74,7 +66,6 @@ public final class NotificationChannelGroup implements Parcelable {
private String mDescription;
private boolean mBlocked;
private List<NotificationChannel> mChannels = new ArrayList<>();
- private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -110,7 +101,6 @@ public final class NotificationChannelGroup implements Parcelable {
}
in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
mBlocked = in.readBoolean();
- mAllowAppOverlay = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -138,7 +128,6 @@ public final class NotificationChannelGroup implements Parcelable {
}
dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
- dest.writeBoolean(mAllowAppOverlay);
dest.writeInt(mUserLockedFields);
}
@@ -181,15 +170,6 @@ public final class NotificationChannelGroup implements Parcelable {
}
/**
- * Returns whether notifications posted to this channel group can display outside of the
- * notification shade, in a floating window on top of other apps. These may additionally be
- * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}.
- */
- public boolean canOverlayApps() {
- return mAllowAppOverlay;
- }
-
- /**
* Sets the user visible description of this group.
*
* <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
@@ -200,21 +180,6 @@ public final class NotificationChannelGroup implements Parcelable {
}
/**
- * Sets whether notifications posted to this channel group can appear outside of the
- * notification shade, floating over other apps' content.
- *
- * <p>This value will be ignored for notifications that are posted to channels that do not
- * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
- *
- * <p>Only modifiable before the channel is submitted to
- * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p>
- * @see Notification#getAppOverlayIntent()
- */
- public void setAllowAppOverlay(boolean allowAppOverlay) {
- mAllowAppOverlay = allowAppOverlay;
- }
-
- /**
* @hide
*/
@TestApi
@@ -266,7 +231,6 @@ public final class NotificationChannelGroup implements Parcelable {
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
setBlocked(safeBool(parser, ATT_BLOCKED, false));
- setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
}
private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
@@ -289,9 +253,6 @@ public final class NotificationChannelGroup implements Parcelable {
out.attribute(null, ATT_DESC, getDescription().toString());
}
out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
- if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
- out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
- }
out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
out.endTag(null, TAG_GROUP);
@@ -307,7 +268,6 @@ public final class NotificationChannelGroup implements Parcelable {
record.put(ATT_NAME, getName());
record.put(ATT_DESC, getDescription());
record.put(ATT_BLOCKED, isBlocked());
- record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
record.put(ATT_USER_LOCKED, mUserLockedFields);
return record;
}
@@ -336,7 +296,6 @@ public final class NotificationChannelGroup implements Parcelable {
if (o == null || getClass() != o.getClass()) return false;
NotificationChannelGroup that = (NotificationChannelGroup) o;
return isBlocked() == that.isBlocked() &&
- mAllowAppOverlay == that.mAllowAppOverlay &&
mUserLockedFields == that.mUserLockedFields &&
Objects.equals(getId(), that.getId()) &&
Objects.equals(getName(), that.getName()) &&
@@ -347,7 +306,7 @@ public final class NotificationChannelGroup implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
- mAllowAppOverlay, mUserLockedFields);
+ mUserLockedFields);
}
@Override
@@ -356,7 +315,6 @@ public final class NotificationChannelGroup implements Parcelable {
cloned.setDescription(getDescription());
cloned.setBlocked(isBlocked());
cloned.setChannels(getChannels());
- cloned.setAllowAppOverlay(canOverlayApps());
cloned.lockFields(mUserLockedFields);
return cloned;
}
@@ -369,7 +327,6 @@ public final class NotificationChannelGroup implements Parcelable {
+ ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
+ ", mBlocked=" + mBlocked
+ ", mChannels=" + mChannels
- + ", mAllowAppOverlay=" + mAllowAppOverlay
+ ", mUserLockedFields=" + mUserLockedFields
+ '}';
}
@@ -385,7 +342,6 @@ public final class NotificationChannelGroup implements Parcelable {
for (NotificationChannel channel : mChannels) {
channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
}
- proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
proto.end(token);
}
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 306c366a86e4..a782ced7f9f8 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1074,6 +1074,25 @@ public class NotificationManager {
}
}
+
+ /**
+ * Sets whether notifications posted by this app can appear outside of the
+ * notification shade, floating over other apps' content.
+ *
+ * <p>This value will be ignored for notifications that are posted to channels that do not
+ * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+ *
+ * @see Notification#getAppOverlayIntent()
+ */
+ public boolean areAppOverlaysAllowed() {
+ INotificationManager service = getService();
+ try {
+ return service.areAppOverlaysAllowed(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Checks the ability to modify notification do not disturb policy for the calling package.
*
@@ -1838,12 +1857,17 @@ public class NotificationManager {
* Recover a list of active notifications: ones that have been posted by the calling app that
* have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
*
- * Each notification is embedded in a {@link StatusBarNotification} object, including the
+ * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
* original <code>tag</code> and <code>id</code> supplied to
* {@link #notify(String, int, Notification) notify()}
* (via {@link StatusBarNotification#getTag() getTag()} and
* {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
* {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+ * </p>
+ * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+ * app's notification delegate via
+ * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+ * </p>
*
* @return An array of {@link StatusBarNotification}.
*/
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index c6086f10bd00..a6f6d06ae71a 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -335,7 +335,8 @@ public class BackupManager {
if (sService != null) {
try {
// All packages, current transport
- IRestoreSession binder = sService.beginRestoreSession(null, null);
+ IRestoreSession binder =
+ sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
if (binder != null) {
session = new RestoreSession(mContext, binder);
}
@@ -465,7 +466,7 @@ public class BackupManager {
checkServiceBinder();
if (sService != null) {
try {
- return sService.getCurrentTransportComponent();
+ return sService.getCurrentTransportComponentForUser(mContext.getUserId());
} catch (RemoteException e) {
Log.e(TAG, "getCurrentTransportComponent() couldn't connect");
}
@@ -530,7 +531,8 @@ public class BackupManager {
checkServiceBinder();
if (sService != null) {
try {
- sService.updateTransportAttributes(
+ sService.updateTransportAttributesForUser(
+ mContext.getUserId(),
transportComponent,
name,
configurationIntent,
@@ -590,7 +592,8 @@ public class BackupManager {
try {
SelectTransportListenerWrapper wrapper = listener == null ?
null : new SelectTransportListenerWrapper(mContext, listener);
- sService.selectBackupTransportAsync(transport, wrapper);
+ sService.selectBackupTransportAsyncForUser(
+ mContext.getUserId(), transport, wrapper);
} catch (RemoteException e) {
Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
}
@@ -637,7 +640,7 @@ public class BackupManager {
checkServiceBinder();
if (sService != null) {
try {
- return sService.getAvailableRestoreToken(packageName);
+ return sService.getAvailableRestoreTokenForUser(mContext.getUserId(), packageName);
} catch (RemoteException e) {
Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
}
@@ -659,7 +662,7 @@ public class BackupManager {
checkServiceBinder();
if (sService != null) {
try {
- return sService.isAppEligibleForBackup(packageName);
+ return sService.isAppEligibleForBackupForUser(mContext.getUserId(), packageName);
} catch (RemoteException e) {
Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
}
@@ -760,7 +763,7 @@ public class BackupManager {
public Intent getConfigurationIntent(String transportName) {
if (sService != null) {
try {
- return sService.getConfigurationIntent(transportName);
+ return sService.getConfigurationIntentForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getConfigurationIntent() couldn't connect");
}
@@ -781,7 +784,7 @@ public class BackupManager {
public String getDestinationString(String transportName) {
if (sService != null) {
try {
- return sService.getDestinationString(transportName);
+ return sService.getDestinationStringForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDestinationString() couldn't connect");
}
@@ -802,7 +805,7 @@ public class BackupManager {
public Intent getDataManagementIntent(String transportName) {
if (sService != null) {
try {
- return sService.getDataManagementIntent(transportName);
+ return sService.getDataManagementIntentForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDataManagementIntent() couldn't connect");
}
@@ -825,7 +828,7 @@ public class BackupManager {
public String getDataManagementLabel(String transportName) {
if (sService != null) {
try {
- return sService.getDataManagementLabel(transportName);
+ return sService.getDataManagementLabelForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDataManagementLabel() couldn't connect");
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f1e6b06e8bac..19de19a820b1 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -43,6 +43,15 @@ interface IBackupManager {
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the caller has made changes to its data.
+ */
+ void dataChangedForUser(int userId, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.dataChangedForUser} for the calling user id.
*/
void dataChanged(String packageName);
@@ -53,6 +62,15 @@ interface IBackupManager {
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backup data should be erased.
+ */
+ void clearBackupDataForUser(int userId, String transportName, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.clearBackupDataForUser} for the calling user id.
*/
void clearBackupData(String transportName, String packageName);
@@ -62,24 +80,59 @@ interface IBackupManager {
* operations.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the given transports should be initialized.
*/
- void initializeTransports(in String[] transportNames, IBackupObserver observer);
+ void initializeTransportsForUser(int userId, in String[] transportNames,
+ IBackupObserver observer);
/**
* Notifies the Backup Manager Service that an agent has become available. This
* method is only invoked by the Activity Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an agent has become available.
+ */
+ void agentConnectedForUser(int userId, String packageName, IBinder agent);
+
+ /**
+ * {@link android.app.backup.IBackupManager.agentConnected} for the calling user id.
*/
void agentConnected(String packageName, IBinder agent);
/**
* Notify the Backup Manager Service that an agent has unexpectedly gone away.
* This method is only invoked by the Activity Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an agent has unexpectedly gone away.
+ */
+ void agentDisconnectedForUser(int userId, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.agentDisconnected} for the calling user id.
*/
void agentDisconnected(String packageName);
/**
* Notify the Backup Manager Service that an application being installed will
* need a data-restore pass. This method is only invoked by the Package Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the application will need a data-restore pass.
+ */
+ void restoreAtInstallForUser(int userId, String packageName, int token);
+
+ /**
+ * {@link android.app.backup.IBackupManager.restoreAtInstallForUser} for the calling user id.
*/
void restoreAtInstall(String packageName, int token);
@@ -112,10 +165,18 @@ interface IBackupManager {
* is made generally available for launch.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which automatic restore should be enabled/disabled.
* @param doAutoRestore When true, enables the automatic app-data restore facility. When
* false, this facility will be disabled.
*/
+ void setAutoRestoreForUser(int userId, boolean doAutoRestore);
+
+ /**
+ * {@link android.app.backup.IBackupManager.setAutoRestoreForUser} for the calling user id.
+ */
void setAutoRestore(boolean doAutoRestore);
/**
@@ -220,9 +281,13 @@ interface IBackupManager {
* Perform a full-dataset backup of the given applications via the currently active
* transport.
*
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the full-dataset backup should be performed.
* @param packageNames The package names of the apps whose data are to be backed up.
*/
- void fullTransportBackup(in String[] packageNames);
+ void fullTransportBackupForUser(int userId, in String[] packageNames);
/**
* Restore device content from the data stream passed through the given socket. The
@@ -250,6 +315,18 @@ interface IBackupManager {
* backup dataset being used for restore.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the requested backup/restore operation can proceed.
+ */
+ void acknowledgeFullBackupOrRestoreForUser(int userId, int token, boolean allow,
+ in String curPassword, in String encryptionPassword,
+ IFullBackupRestoreObserver observer);
+
+ /**
+ * {@link android.app.backup.IBackupManager.acknowledgeFullBackupOrRestoreForUser} for the
+ * calling user id.
*/
void acknowledgeFullBackupOrRestore(int token, boolean allow,
in String curPassword, in String encryptionPassword,
@@ -260,7 +337,10 @@ interface IBackupManager {
* specified transport has not been bound at least once (for registration), this call will be
* ignored. Only the host process of the transport can change its description, otherwise a
* {@link SecurityException} will be thrown.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the attributes of the transport should be updated.
* @param transportComponent The identity of the transport being described.
* @param name A {@link String} with the new name for the transport. This is NOT for
* identification. MUST NOT be {@code null}.
@@ -279,13 +359,23 @@ interface IBackupManager {
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
- void updateTransportAttributes(in ComponentName transportComponent, in String name,
+ void updateTransportAttributesForUser(int userId, in ComponentName transportComponent,
+ in String name,
in Intent configurationIntent, in String currentDestinationString,
in Intent dataManagementIntent, in String dataManagementLabel);
/**
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the currently selected transport should be identified.
+ */
+ String getCurrentTransportForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getCurrentTransportForUser} for the calling user id.
*/
String getCurrentTransport();
@@ -293,16 +383,35 @@ interface IBackupManager {
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered. Callers must
* hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the currently selected transport should be identified.
*/
- ComponentName getCurrentTransportComponent();
+ ComponentName getCurrentTransportComponentForUser(int userId);
/**
* Request a list of all available backup transports' names. Callers must
* hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which all available backup transports' names should be listed.
+ */
+ String[] listAllTransportsForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.listAllTransportsForUser} for the calling user id.
*/
String[] listAllTransports();
- ComponentName[] listAllTransportComponents();
+ /**
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which all available backup transports should be listed.
+ */
+ ComponentName[] listAllTransportComponentsForUser(int userId);
/**
* Retrieve the list of whitelisted transport components. Callers do </i>not</i> need
@@ -315,13 +424,22 @@ interface IBackupManager {
/**
* Specify the current backup transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport should be selected.
* @param transport The name of the transport to select. This should be one
* of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
* @return The name of the previously selected transport. If the given transport
* name is not one of the currently available transports, no change is made to
* the current transport setting and the method returns null.
*/
+ String selectBackupTransportForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.selectBackupTransportForUser} for the calling user
+ * id.
+ */
String selectBackupTransport(String transport);
/**
@@ -330,43 +448,85 @@ interface IBackupManager {
* which is in a separate process.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport should be selected.
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
* @param listener A listener object to get a callback on the transport being selected.
*/
- void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
+ void selectBackupTransportAsyncForUser(int userId, in ComponentName transport,
+ ISelectBackupTransportCallback listener);
/**
* Get the configuration Intent, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the configuration Intent should be reported.
* @param transport The name of the transport to query.
* @return An Intent to use with Activity#startActivity() to bring up the configuration
* UI supplied by the transport. If the transport has no configuration UI, it should
* return {@code null} here.
*/
+ Intent getConfigurationIntentForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getConfigurationIntentForUser} for the calling user
+ * id.
+ */
Intent getConfigurationIntent(String transport);
/**
* Get the destination string supplied by the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport destination string should be reported.
* @param transport The name of the transport to query.
* @return A string describing the current backup destination. This string is used
* verbatim by the Settings UI as the summary text of the "configure..." item.
*/
+ String getDestinationStringForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDestinationStringForUser} for the calling user
+ * id.
+ */
String getDestinationString(String transport);
/**
* Get the manage-data UI intent, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the manage-data UI intent should be reported.
+ */
+ Intent getDataManagementIntentForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDataManagementIntentForUser} for the calling user
+ * id.
*/
Intent getDataManagementIntent(String transport);
/**
* Get the manage-data menu label, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the manage-data menu label should be reported.
+ */
+ String getDataManagementLabelForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDataManagementLabelForUser} for the calling user
+ * id.
*/
String getDataManagementLabel(String transport);
@@ -381,7 +541,10 @@ interface IBackupManager {
* package. In that case, the restore session returned is suitable for supporting
* the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
* without requiring the app to hold any special permission.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which a restore session should be begun.
* @param packageName The name of the single package for which a restore will
* be requested. May be null, in which case all packages in the restore
* set can be restored.
@@ -389,7 +552,7 @@ interface IBackupManager {
* May be null, in which case the current active transport is used.
* @return An interface to the restore session, or null on error.
*/
- IRestoreSession beginRestoreSession(String packageName, String transportID);
+ IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
/**
* Notify the backup manager that a BackupAgent has completed the operation
@@ -427,13 +590,16 @@ interface IBackupManager {
* restored from if we were to install it right now.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which this operation should be performed.
* @param packageName The name of the package whose most-suitable dataset we
* wish to look up
* @return The dataset token from which a restore should be attempted, or zero if
* no suitable data is available.
*/
- long getAvailableRestoreToken(String packageName);
+ long getAvailableRestoreTokenForUser(int userId, String packageName);
/**
* Ask the framework whether this app is eligible for backup.
@@ -442,21 +608,27 @@ interface IBackupManager {
* {@link #filterAppsEligibleForBackup(String[])} to save resources.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which this operation should be performed.
* @param packageName The name of the package.
* @return Whether this app is eligible for backup.
*/
- boolean isAppEligibleForBackup(String packageName);
+ boolean isAppEligibleForBackupForUser(int userId, String packageName);
/**
* Filter the packages that are eligible for backup and return the result.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the filter should be performed.
* @param packages The list of packages to filter.
* @return The packages eligible for backup.
*/
- String[] filterAppsEligibleForBackup(in String[] packages);
+ String[] filterAppsEligibleForBackupForUser(int userId, in String[] packages);
/**
* Request an immediate backup, providing an observer to which results of the backup operation
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e08d405324ea..adedff3e9386 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,7 +67,9 @@ public final class BluetoothManager {
}
// Legacy api - getDefaultAdapter does not take in the context
mAdapter = BluetoothAdapter.getDefaultAdapter();
- mAdapter.setContext(context);
+ if (mAdapter != null) {
+ mAdapter.setContext(context);
+ }
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2f0618cf64b3..d5c6c63243f6 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4222,6 +4222,11 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
/**
+ * The home activity shown on secondary displays that support showing home activities.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
+ /**
* This is the setup wizard activity, that is the first activity that is displayed
* when the user sets up the device for the first time.
* @hide
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f81eb7642443..623bdda93b7e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,6 +55,8 @@ import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Iterator;
@@ -1594,6 +1597,29 @@ public class PackageInstaller {
public static final int INVALID_ID = -1;
/** {@hide} */
private static final int[] NO_SESSIONS = {};
+
+ /** @hide */
+ @IntDef(value = {NO_ERROR, VERIFICATION_FAILED, ACTIVATION_FAILED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StagedSessionErrorCode{}
+ /**
+ * Constant indicating that no error occurred during the preparation or the activation of
+ * this staged session.
+ */
+ public static final int NO_ERROR = 0;
+
+ /**
+ * Constant indicating that an error occurred during the verification phase (pre-reboot) of
+ * this staged session.
+ */
+ public static final int VERIFICATION_FAILED = 1;
+
+ /**
+ * Constant indicating that an error occurred during the activation phase (post-reboot) of
+ * this staged session.
+ */
+ public static final int ACTIVATION_FAILED = 2;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
@@ -1653,6 +1679,14 @@ public class PackageInstaller {
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
+ public boolean isSessionApplied;
+ /** {@hide} */
+ public boolean isSessionReady;
+ /** {@hide} */
+ public boolean isSessionFailed;
+ private int mStagedSessionErrorCode;
+
+ /** {@hide} */
@UnsupportedAppUsage
public SessionInfo() {
}
@@ -1686,6 +1720,10 @@ public class PackageInstaller {
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
+ isSessionApplied = source.readBoolean();
+ isSessionReady = source.readBoolean();
+ isSessionFailed = source.readBoolean();
+ mStagedSessionErrorCode = source.readInt();
}
/**
@@ -1970,6 +2008,44 @@ public class PackageInstaller {
return childSessionIds;
}
+ /**
+ * Whether the staged session has been applied successfully, meaning that all of its
+ * packages have been activated and no further action is required.
+ * Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionApplied() {
+ return isSessionApplied;
+ }
+
+ /**
+ * Whether the staged session is ready to be applied at next reboot. Only meaningful if
+ * {@code isStaged} is true.
+ */
+ public boolean isSessionReady() {
+ return isSessionReady;
+ }
+
+ /**
+ * Whether something went wrong and the staged session is declared as failed, meaning that
+ * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionFailed() {
+ return isSessionFailed;
+ }
+
+ /**
+ * If something went wrong with a staged session, clients can check this error code to
+ * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
+ */
+ public int getStagedSessionErrorCode() {
+ return mStagedSessionErrorCode;
+ }
+
+ /** {@hide} */
+ public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode) {
+ mStagedSessionErrorCode = errorCode;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2001,6 +2077,10 @@ public class PackageInstaller {
dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
+ dest.writeBoolean(isSessionApplied);
+ dest.writeBoolean(isSessionReady);
+ dest.writeBoolean(isSessionFailed);
+ dest.writeInt(mStagedSessionErrorCode);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 566017b7372e..9d604bbd75aa 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2288,21 +2288,28 @@ public abstract class PackageManager {
* {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ public static final String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_FACE = "android.hardware.face";
+ public static final String FEATURE_FACE = "android.hardware.biometrics.face";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_IRIS = "android.hardware.iris";
+ public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
/**
* Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca331f9f..be054297c769 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@ package android.hardware.display;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
/** @hide */
@SystemApi
@TestApi
public final class BrightnessConfiguration implements Parcelable {
+ private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+ private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+ private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+ private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_NITS = "nits";
+ private static final String ATTR_DESCRIPTION = "description";
+ private static final String ATTR_PACKAGE_NAME = "package-name";
+ private static final String ATTR_CATEGORY = "category";
+
private final float[] mLux;
private final float[] mNits;
+ private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private final String mDescription;
- private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+ private BrightnessConfiguration(float[] lux, float[] nits,
+ Map<String, BrightnessCorrection> correctionsByPackageName,
+ Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
mLux = lux;
mNits = nits;
+ mCorrectionsByPackageName = correctionsByPackageName;
+ mCorrectionsByCategory = correctionsByCategory;
mDescription = description;
}
@@ -56,6 +84,38 @@ public final class BrightnessConfiguration implements Parcelable {
}
/**
+ * Returns a brightness correction by app, or null.
+ *
+ * @param packageName
+ * The app's package name.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+ return mCorrectionsByPackageName.get(packageName);
+ }
+
+ /**
+ * Returns a brightness correction by app category, or null.
+ *
+ * @param category
+ * The app category.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByCategory(int category) {
+ return mCorrectionsByCategory.get(category);
+ }
+
+ /**
* Returns description string.
* @hide
*/
@@ -67,6 +127,20 @@ public final class BrightnessConfiguration implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloatArray(mLux);
dest.writeFloatArray(mNits);
+ dest.writeInt(mCorrectionsByPackageName.size());
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeString(packageName);
+ correction.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mCorrectionsByCategory.size());
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeInt(category);
+ correction.writeToParcel(dest, flags);
+ }
dest.writeString(mDescription);
}
@@ -85,7 +159,14 @@ public final class BrightnessConfiguration implements Parcelable {
}
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
}
- sb.append("], '");
+ sb.append("], {");
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+ }
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+ }
+ sb.append("}, '");
if (mDescription != null) {
sb.append(mDescription);
}
@@ -98,6 +179,8 @@ public final class BrightnessConfiguration implements Parcelable {
int result = 1;
result = result * 31 + Arrays.hashCode(mLux);
result = result * 31 + Arrays.hashCode(mNits);
+ result = result * 31 + mCorrectionsByPackageName.hashCode();
+ result = result * 31 + mCorrectionsByCategory.hashCode();
if (mDescription != null) {
result = result * 31 + mDescription.hashCode();
}
@@ -114,6 +197,8 @@ public final class BrightnessConfiguration implements Parcelable {
}
final BrightnessConfiguration other = (BrightnessConfiguration) o;
return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+ && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+ && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
&& Objects.equals(mDescription, other.mDescription);
}
@@ -123,7 +208,25 @@ public final class BrightnessConfiguration implements Parcelable {
float[] lux = in.createFloatArray();
float[] nits = in.createFloatArray();
Builder builder = new Builder(lux, nits);
- builder.setDescription(in.readString());
+
+ int n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final String packageName = in.readString();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+
+ n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final int category = in.readInt();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByCategory(category, correction);
+ }
+
+ final String description = in.readString();
+ builder.setDescription(description);
return builder.build();
}
@@ -133,11 +236,146 @@ public final class BrightnessConfiguration implements Parcelable {
};
/**
+ * Writes the configuration to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+ if (mDescription != null) {
+ serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+ }
+ for (int i = 0; i < mLux.length; i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+ serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+ serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+ serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ for (Map.Entry<String, BrightnessCorrection> entry :
+ mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ }
+
+ /**
+ * Read a configuration from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String description = null;
+ List<Float> luxList = new ArrayList<>();
+ List<Float> nitsList = new ArrayList<>();
+ Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+ Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+ final int configDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, configDepth)) {
+ if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+ description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+ final int curveDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+ if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+ continue;
+ }
+ final float lux = loadFloatFromXml(parser, ATTR_LUX);
+ final float nits = loadFloatFromXml(parser, ATTR_NITS);
+ luxList.add(lux);
+ nitsList.add(nits);
+ }
+ }
+ if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+ final int correctionsDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+ if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+ continue;
+ }
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+ BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+ if (packageName != null) {
+ correctionsByPackageName.put(packageName, correction);
+ } else if (categoryText != null) {
+ try {
+ final int category = Integer.parseInt(categoryText);
+ correctionsByCategory.put(category, correction);
+ } catch (NullPointerException | NumberFormatException e) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ final int n = luxList.size();
+ float[] lux = new float[n];
+ float[] nits = new float[n];
+ for (int i = 0; i < n; i++) {
+ lux[i] = luxList.get(i);
+ nits[i] = nitsList.get(i);
+ }
+ final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+ nits);
+ builder.setDescription(description);
+ for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByCategory(category, correction);
+ }
+ return builder.build();
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ /**
* A builder class for {@link BrightnessConfiguration}s.
*/
public static class Builder {
+ private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+ private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
private float[] mCurveLux;
private float[] mCurveNits;
+ private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private String mDescription;
/**
@@ -169,6 +407,88 @@ public final class BrightnessConfiguration implements Parcelable {
checkMonotonic(nits, false /*strictly increasing*/, "nits");
mCurveLux = lux;
mCurveNits = nits;
+ mCorrectionsByPackageName = new HashMap<>();
+ mCorrectionsByCategory = new HashMap<>();
+ }
+
+ /**
+ * Returns the maximum number of corrections by package name allowed.
+ *
+ * @return The maximum number of corrections by package name allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByPackageName() {
+ return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+ }
+
+ /**
+ * Returns the maximum number of corrections by category allowed.
+ *
+ * @return The maximum number of corrections by category allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByCategory() {
+ return MAX_CORRECTIONS_BY_CATEGORY;
+ }
+
+ /**
+ * Add a brightness correction by app package name.
+ * This correction is applied whenever an app with this package name has the top activity
+ * of the focused stack.
+ *
+ * @param packageName
+ * The app's package name.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentExceptions
+ * Maximum number of corrections by package name exceeded (see
+ * {@link #getMaxCorrectionsByPackageName}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByPackageName(String packageName,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+ throw new IllegalArgumentException("Too many corrections by package name");
+ }
+ mCorrectionsByPackageName.put(packageName, correction);
+ return this;
+ }
+
+ /**
+ * Add a brightness correction by app category.
+ * This correction is applied whenever an app with this category has the top activity of
+ * the focused stack, and only if a correction by package name has not been applied.
+ *
+ * @param category
+ * The {@link android.content.pm.ApplicationInfo#category app category}.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException
+ * Maximum number of corrections by category exceeded (see
+ * {@link #getMaxCorrectionsByCategory}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+ throw new IllegalArgumentException("Too many corrections by category");
+ }
+ mCorrectionsByCategory.put(category, correction);
+ return this;
}
/**
@@ -191,7 +511,8 @@ public final class BrightnessConfiguration implements Parcelable {
if (mCurveLux == null || mCurveNits == null) {
throw new IllegalStateException("A curve must be set!");
}
- return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+ return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+ mCorrectionsByCategory, mDescription);
}
private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
new file mode 100644
index 000000000000..3abe29cc0076
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 000000000000..c4e0e3b723cd
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+ private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+ private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+ private BrightnessCorrectionImplementation mImplementation;
+
+ // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+ // make this class abstract and use composition instead of inheritence.
+ private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+ mImplementation = implementation;
+ }
+
+ /**
+ * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @param scale
+ * How much to scale the log brightness.
+ * @param translate
+ * How much to translate the log brightness.
+ *
+ * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @throws IllegalArgumentException
+ * - scale or translate are NaN.
+ */
+ @NonNull
+ public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+ BrightnessCorrectionImplementation implementation =
+ new ScaleAndTranslateLog(scale, translate);
+ return new BrightnessCorrection(implementation);
+ }
+
+ /**
+ * Applies the brightness correction to a given brightness.
+ *
+ * @param brightness
+ * The brightness.
+ *
+ * @return The corrected brightness.
+ */
+ public float apply(float brightness) {
+ return mImplementation.apply(brightness);
+ }
+
+ /**
+ * Returns a string representation.
+ *
+ * @return A string representation.
+ */
+ public String toString() {
+ return mImplementation.toString();
+ }
+
+ public static final Creator<BrightnessCorrection> CREATOR =
+ new Creator<BrightnessCorrection>() {
+ public BrightnessCorrection createFromParcel(Parcel in) {
+ final int type = in.readInt();
+ switch (type) {
+ case SCALE_AND_TRANSLATE_LOG:
+ return ScaleAndTranslateLog.readFromParcel(in);
+ }
+ return null;
+ }
+
+ public BrightnessCorrection[] newArray(int size) {
+ return new BrightnessCorrection[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mImplementation.writeToParcel(dest);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the correction to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ mImplementation.saveToXml(serializer);
+ }
+
+ /**
+ * Read a correction from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+ return ScaleAndTranslateLog.loadFromXml(parser);
+ }
+ }
+ return null;
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ private interface BrightnessCorrectionImplementation {
+ float apply(float brightness);
+ String toString();
+ void writeToParcel(Parcel dest);
+ void saveToXml(XmlSerializer serializer) throws IOException;
+ // Package-private static methods:
+ // static BrightnessCorrection readFromParcel(Parcel in);
+ // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ // XmlPullParserException;
+ }
+
+ /**
+ * A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ */
+ private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+ private static final float MIN_SCALE = 0.5f;
+ private static final float MAX_SCALE = 2.0f;
+ private static final float MIN_TRANSLATE = -0.6f;
+ private static final float MAX_TRANSLATE = 0.7f;
+
+ private static final String ATTR_SCALE = "scale";
+ private static final String ATTR_TRANSLATE = "translate";
+
+ private final float mScale;
+ private final float mTranslate;
+
+ ScaleAndTranslateLog(float scale, float translate) {
+ if (Float.isNaN(scale) || Float.isNaN(translate)) {
+ throw new IllegalArgumentException("scale and translate must be numbers");
+ }
+ mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+ }
+
+ @Override
+ public float apply(float brightness) {
+ return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+ }
+
+ @Override
+ public String toString() {
+ return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+ dest.writeFloat(mScale);
+ dest.writeFloat(mTranslate);
+ }
+
+ @Override
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+ serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+ serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ }
+
+ static BrightnessCorrection readFromParcel(Parcel in) {
+ float scale = in.readFloat();
+ float translate = in.readFloat();
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+
+ static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+ final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+ }
+}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index f4e776cdb4b9..edc3f9466efc 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbPort;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -112,7 +112,7 @@ interface IUsbManager
ParcelFileDescriptor getControlFd(long function);
/* Gets the list of USB ports. */
- UsbPort[] getPorts();
+ List<ParcelableUsbPort> getPorts();
/* Gets the status of the specified USB port. */
UsbPortStatus getPortStatus(in String portId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
index b7a79202914e..4431551fec92 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
package android.hardware.usb;
-parcelable UsbPort;
+parcelable ParcelableUsbPort;
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
new file mode 100644
index 000000000000..7f7ba96b40f2
--- /dev/null
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * A parcelable wrapper to send UsbPorts over binders.
+ *
+ * @hide
+ */
+@Immutable
+public final class ParcelableUsbPort implements Parcelable {
+ private final @NonNull String mId;
+ private final int mSupportedModes;
+
+ private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+ mId = id;
+ mSupportedModes = supportedModes;
+ }
+
+ /**
+ * Create the parcelable version of a {@link UsbPort}.
+ *
+ * @param port The port to create a parcealable version of
+ *
+ * @return The parcelable version of the port
+ */
+ public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
+ return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+ }
+
+ /**
+ * Create a {@link UsbPort} from this object.
+ *
+ * @param usbManager A link to the usbManager in the current context
+ *
+ * @return The UsbPort for this object
+ */
+ public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
+ return new UsbPort(usbManager, mId, mSupportedModes);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mSupportedModes);
+ }
+
+ public static final Creator<ParcelableUsbPort> CREATOR =
+ new Creator<ParcelableUsbPort>() {
+ @Override
+ public ParcelableUsbPort createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ return new ParcelableUsbPort(id, supportedModes);
+ }
+
+ @Override
+ public ParcelableUsbPort[] newArray(int size) {
+ return new ParcelableUsbPort[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 41119416e419..601447814952 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
package android.hardware.usb;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -39,9 +40,10 @@ import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
@@ -97,15 +99,11 @@ public class UsbManager {
* Broadcast Action: A broadcast for USB port changes.
*
* This intent is sent when a USB port is added, removed, or changes state.
- * <ul>
- * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
- * for the port.
- * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
- * for the port, or null if the port has been removed
- * </ul>
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
public static final String ACTION_USB_PORT_CHANGED =
"android.hardware.usb.action.USB_PORT_CHANGED";
@@ -796,34 +794,44 @@ public class UsbManager {
* device class (which supports all types of ports despite its name).
* </p>
*
- * @return The list of USB ports, or null if none.
+ * @return The list of USB ports
*
* @hide
*/
- @UnsupportedAppUsage
- public UsbPort[] getPorts() {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @NonNull List<UsbPort> getPorts() {
if (mService == null) {
- return null;
+ return Collections.emptyList();
}
+
+ List<ParcelableUsbPort> parcelablePorts;
try {
- return mService.getPorts();
+ parcelablePorts = mService.getPorts();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (parcelablePorts == null) {
+ return Collections.emptyList();
+ } else {
+ int numPorts = parcelablePorts.size();
+
+ ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
+ for (int i = 0; i < numPorts; i++) {
+ ports.add(parcelablePorts.get(i).getUsbPort(this));
+ }
+
+ return ports;
+ }
}
/**
- * Gets the status of the specified USB port.
- *
- * @param port The port to query.
- * @return The status of the specified USB port, or null if unknown.
+ * Should only be called by {@link UsbPort#getStatus}.
*
* @hide
*/
- @UnsupportedAppUsage
- public UsbPortStatus getPortStatus(UsbPort port) {
- Preconditions.checkNotNull(port, "port must not be null");
-
+ UsbPortStatus getPortStatus(UsbPort port) {
try {
return mService.getPortStatus(port.getId());
} catch (RemoteException e) {
@@ -832,29 +840,11 @@ public class UsbManager {
}
/**
- * Sets the desired role combination of the port.
- * <p>
- * The supported role combinations depend on what is connected to the port and may be
- * determined by consulting
- * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
- * </p><p>
- * Note: This function is asynchronous and may fail silently without applying
- * the requested changes. If this function does cause a status change to occur then
- * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
- * </p>
- *
- * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * Should only be called by {@link UsbPort#setRoles}.
*
* @hide
*/
- @UnsupportedAppUsage
- public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
- Preconditions.checkNotNull(port, "port must not be null");
- UsbPort.checkRoles(powerRole, dataRole);
-
+ void setPortRoles(UsbPort port, int powerRole, int dataRole) {
Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
try {
mService.setPortRoles(port.getId(), powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index afdb202211dd..37154e4c81b2 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,104 +16,53 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.hardware.usb.V1_0.Constants;
-import android.os.Parcel;
-import android.os.Parcelable;
import com.android.internal.util.Preconditions;
/**
* Represents a physical USB port and describes its characteristics.
- * <p>
- * This object is immutable.
- * </p>
*
* @hide
*/
-public final class UsbPort implements Parcelable {
+@SystemApi
+public final class UsbPort {
private final String mId;
private final int mSupportedModes;
-
- public static final int MODE_NONE = Constants.PortMode.NONE;
- /**
- * Mode bit: This USB port can act as a downstream facing port (host).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_DFP = Constants.PortMode.DFP;
-
- /**
- * Mode bit: This USB port can act as an upstream facing port (device).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_UFP = Constants.PortMode.UFP;
-
- /**
- * Mode bit: This USB port can act either as an downstream facing port (host) or as
- * an upstream facing port (device).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
- * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_DUAL = Constants.PortMode.DRP;
-
- /**
- * Mode bit: This USB port can support USB Type-C Audio accessory.
- */
- public static final int MODE_AUDIO_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
-
- /**
- * Mode bit: This USB port can support USB Type-C debug accessory.
- */
- public static final int MODE_DEBUG_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
-
- /**
- * Power role: This USB port does not have a power role.
- */
- public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
-
- /**
- * Power role: This USB port can act as a source (provide power).
- */
- public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
-
- /**
- * Power role: This USB port can act as a sink (receive power).
- */
- public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
-
- /**
- * Power role: This USB port does not have a data role.
- */
- public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
-
- /**
- * Data role: This USB port can act as a host (access data services).
- */
- public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
-
- /**
- * Data role: This USB port can act as a device (offer data services).
- */
- public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+ private final UsbManager mUsbManager;
private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+
/**
* Points to the first power role in the IUsb HAL.
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
/** @hide */
- public UsbPort(String id, int supportedModes) {
+ public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+ Preconditions.checkNotNull(id);
+ Preconditions.checkFlagsArgument(supportedModes,
+ MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
+
+ mUsbManager = usbManager;
mId = id;
mSupportedModes = supportedModes;
}
@@ -122,6 +71,8 @@ public final class UsbPort implements Parcelable {
* Gets the unique id of the port.
*
* @return The unique id of the port; not intended for display.
+ *
+ * @hide
*/
public String getId() {
return mId;
@@ -133,23 +84,62 @@ public final class UsbPort implements Parcelable {
* The actual mode of the port may vary depending on what is plugged into it.
* </p>
*
- * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
- * {@link #MODE_DUAL}.
+ * @return The supported modes: one of {@link UsbPortStatus#MODE_DFP},
+ * {@link UsbPortStatus#MODE_UFP}, or {@link UsbPortStatus#MODE_DUAL}.
+ *
+ * @hide
*/
public int getSupportedModes() {
return mSupportedModes;
}
/**
+ * Gets the status of this USB port.
+ *
+ * @return The status of the this port, or {@code null} if port is unknown.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @Nullable UsbPortStatus getStatus() {
+ return mUsbManager.getPortStatus(this);
+ }
+
+ /**
+ * Sets the desired role combination of the port.
+ * <p>
+ * The supported role combinations depend on what is connected to the port and may be
+ * determined by consulting
+ * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
+ * </p><p>
+ * Note: This function is asynchronous and may fail silently without applying
+ * the requested changes. If this function does cause a status change to occur then
+ * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
+ * </p>
+ *
+ * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE} or
+ * {@link UsbPortStatus#POWER_ROLE_SINK}, or
+ * {@link UsbPortStatus#POWER_ROLE_NONE} if no power role.
+ * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST} or
+ * {@link UsbPortStatus#DATA_ROLE_DEVICE}, or
+ * {@link UsbPortStatus#DATA_ROLE_NONE} if no data role.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public void setRoles(@UsbPortStatus.UsbPowerRole int powerRole,
+ @UsbPortStatus.UsbDataRole int dataRole) {
+ UsbPort.checkRoles(powerRole, dataRole);
+
+ mUsbManager.setPortRoles(this, powerRole, dataRole);
+ }
+
+ /**
* Combines one power and one data role together into a unique value with
* exactly one bit set. This can be used to efficiently determine whether
* a combination of roles is supported by testing whether that bit is present
* in a bit-field.
*
- * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE}
+ * or {@link UsbPortStatus#POWER_ROLE_SINK}, or 0 if no power role.
+ * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST}
+ * or {@link UsbPortStatus#DATA_ROLE_DEVICE}, or 0 if no data role.
* @hide
*/
public static int combineRolesAsBit(int powerRole, int dataRole) {
@@ -276,30 +266,4 @@ public final class UsbPort implements Parcelable {
public String toString() {
return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mId);
- dest.writeInt(mSupportedModes);
- }
-
- public static final Parcelable.Creator<UsbPort> CREATOR =
- new Parcelable.Creator<UsbPort>() {
- @Override
- public UsbPort createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- return new UsbPort(id, supportedModes);
- }
-
- @Override
- public UsbPort[] newArray(int size) {
- return new UsbPort[size];
- }
- };
}
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 2cd8209fccda..d30201a597bf 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,27 +16,134 @@
package android.hardware.usb;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.usb.V1_0.Constants;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Describes the status of a USB port.
- * <p>
- * This object is immutable.
- * </p>
*
* @hide
*/
+@Immutable
+@SystemApi
public final class UsbPortStatus implements Parcelable {
private final int mCurrentMode;
- private final int mCurrentPowerRole;
- private final int mCurrentDataRole;
+ private final @UsbPowerRole int mCurrentPowerRole;
+ private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
+ /**
+ * Power role: This USB port does not have a power role.
+ */
+ public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+
+ /**
+ * Power role: This USB port can act as a source (provide power).
+ */
+ public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+
+ /**
+ * Power role: This USB port can act as a sink (receive power).
+ */
+ public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+ @IntDef(prefix = { "POWER_ROLE_" }, value = {
+ POWER_ROLE_NONE,
+ POWER_ROLE_SOURCE,
+ POWER_ROLE_SINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbPowerRole{}
+
+ /**
+ * Power role: This USB port does not have a data role.
+ */
+ public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+
+ /**
+ * Data role: This USB port can act as a host (access data services).
+ */
+ public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+
+ /**
+ * Data role: This USB port can act as a device (offer data services).
+ */
+ public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+
+ @IntDef(prefix = { "DATA_ROLE_" }, value = {
+ DATA_ROLE_NONE,
+ DATA_ROLE_HOST,
+ DATA_ROLE_DEVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbDataRole{}
+
+ /**
+ * There is currently nothing connected to this USB port.
+ */
+ public static final int MODE_NONE = Constants.PortMode.NONE;
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_DFP = Constants.PortMode.DFP;
+
+ /**
+ * This USB port can act as an upstream facing port (device).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+ * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_UFP = Constants.PortMode.UFP;
+
+ /**
+ * This USB port can act either as an downstream facing port (host) or as
+ * an upstream facing port (device).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles and the {@link #POWER_ROLE_SINK} and
+ * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+ *
+ * @hide
+ */
+ public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+ /**
+ * This USB port can support USB Type-C Audio accessory.
+ */
+ public static final int MODE_AUDIO_ACCESSORY =
+ android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+
+ /**
+ * This USB port can support USB Type-C debug accessory.
+ */
+ public static final int MODE_DEBUG_ACCESSORY =
+ android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+
+ @IntDef(prefix = { "MODE_" }, flag = true, value = {
+ MODE_NONE,
+ MODE_DFP,
+ MODE_UFP,
+ MODE_AUDIO_ACCESSORY,
+ MODE_DEBUG_ACCESSORY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbPortMode{}
+
/** @hide */
- public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
- int supportedRoleCombinations) {
+ public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
+ @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
mCurrentDataRole = currentDataRole;
@@ -46,9 +153,8 @@ public final class UsbPortStatus implements Parcelable {
/**
* Returns true if there is anything connected to the port.
*
- * @return True if there is anything connected to the port.
+ * @return {@code true} iff there is anything connected to the port.
*/
- @UnsupportedAppUsage
public boolean isConnected() {
return mCurrentMode != 0;
}
@@ -56,33 +162,31 @@ public final class UsbPortStatus implements Parcelable {
/**
* Gets the current mode of the port.
*
- * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
- * or 0 if nothing is connected.
+ * @return The current mode: {@link #MODE_DFP}, {@link #MODE_UFP},
+ * {@link #MODE_AUDIO_ACCESSORY}, {@link #MODE_DEBUG_ACCESSORY}, or {@link {@link #MODE_NONE} if
+ * nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentMode() {
+ public @UsbPortMode int getCurrentMode() {
return mCurrentMode;
}
/**
* Gets the current power role of the port.
*
- * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
- * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+ * @return The current power role: {@link #POWER_ROLE_SOURCE}, {@link #POWER_ROLE_SINK}, or
+ * {@link #POWER_ROLE_NONE} if nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentPowerRole() {
+ public @UsbPowerRole int getCurrentPowerRole() {
return mCurrentPowerRole;
}
/**
* Gets the current data role of the port.
*
- * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
- * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+ * @return The current data role: {@link #DATA_ROLE_HOST}, {@link #DATA_ROLE_DEVICE}, or
+ * {@link #DATA_ROLE_NONE} if nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentDataRole() {
+ public @UsbDataRole int getCurrentDataRole() {
return mCurrentDataRole;
}
@@ -90,19 +194,20 @@ public final class UsbPortStatus implements Parcelable {
* Returns true if the specified power and data role combination is supported
* given what is currently connected to the port.
*
- * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * @param powerRole The power role to check: {@link #POWER_ROLE_SOURCE} or
+ * {@link #POWER_ROLE_SINK}, or {@link #POWER_ROLE_NONE} if no power role.
+ * @param dataRole The data role to check: either {@link #DATA_ROLE_HOST} or
+ * {@link #DATA_ROLE_DEVICE}, or {@link #DATA_ROLE_NONE} if no data role.
*/
- @UnsupportedAppUsage
- public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+ public boolean isRoleCombinationSupported(@UsbPowerRole int powerRole,
+ @UsbDataRole int dataRole) {
return (mSupportedRoleCombinations &
UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * Get the supported role combinations.
+ */
public int getSupportedRoleCombinations() {
return mSupportedRoleCombinations;
}
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 4b1a08ded9d6..0877a1a47e2b 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -45,6 +45,20 @@ oneway interface INetdEventCallback {
in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
/**
+ * Represents adding or removing a NAT64 prefix.
+ * This method must not block or perform long-running operations.
+ *
+ * @param netId the ID of the network the prefix was performed on.
+ * @param added true if the NAT64 prefix was added, or false if the NAT64 prefix was removed.
+ * There is only one prefix at a time for each netId. If a prefix is added, it replaces
+ * the previous-added prefix.
+ * @param prefixString the detected NAT64 prefix as a string literal.
+ * @param prefixLength the prefix length associated with this NAT64 prefix.
+ */
+ void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString,
+ int prefixLength);
+
+ /**
* Represents a private DNS validation success or failure.
* This method must not block or perform long-running operations.
*
diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
new file mode 100644
index 000000000000..8e6c69a97edb
--- /dev/null
+++ b/core/java/android/net/InetAddresses.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+ private InetAddresses() {}
+
+ /**
+ * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+ * {@code "2001:db8::1:2"}).
+ *
+ * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+ * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+ * do not have exactly 4 numbers are not treated as numeric.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse.
+ * @return true if the supplied address is numeric, false otherwise.
+ */
+ public static boolean isNumericAddress(String address) {
+ return InetAddressUtils.isNumericAddress(address);
+ }
+
+ /**
+ * Returns an InetAddress corresponding to the given numeric address (such
+ * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+ *
+ * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+ * numeric address.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse, must be numeric.
+ * @return an {@link InetAddress} instance corresponding to the address.
+ * @throws IllegalArgumentException if {@code address} is not a numeric address.
+ */
+ public static InetAddress parseNumericAddress(String address) {
+ return InetAddressUtils.parseNumericAddress(address);
+ }
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8a5f43de6883..c41a56c7305f 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1467,7 +1467,7 @@ public final class NetworkCapabilities implements Parcelable {
appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
NetworkCapabilities::capabilityNameOf, "&");
}
- if (0 != mNetworkCapabilities) {
+ if (0 != mUnwantedNetworkCapabilities) {
sb.append(" Unwanted: ");
appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
NetworkCapabilities::capabilityNameOf, "&");
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c431e40ede2f..4eab49cd0fdf 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@ import static android.system.OsConstants.AF_INET6;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.system.Os;
import android.util.Log;
@@ -299,8 +300,10 @@ public class NetworkUtils {
* @param addrString
* @return the InetAddress
* @hide
+ * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @Deprecated
public static InetAddress numericToInetAddress(String addrString)
throws IllegalArgumentException {
return InetAddress.parseNumericAddress(addrString);
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 428d9e156e37..e84a518c4986 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -23,6 +23,7 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_ACCMODE;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
@@ -1259,11 +1260,11 @@ public class FileUtils {
int res = 0;
if (mode.startsWith("rw")) {
- res |= O_RDWR | O_CREAT;
+ res = O_RDWR | O_CREAT;
} else if (mode.startsWith("w")) {
- res |= O_WRONLY | O_CREAT;
+ res = O_WRONLY | O_CREAT;
} else if (mode.startsWith("r")) {
- res |= O_RDONLY;
+ res = O_RDONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1279,12 +1280,12 @@ public class FileUtils {
/** {@hide} */
public static String translateModePosixToString(int mode) {
String res = "";
- if ((mode & O_RDWR) == O_RDWR) {
- res += "rw";
- } else if ((mode & O_WRONLY) == O_WRONLY) {
- res += "w";
- } else if ((mode & O_RDONLY) == O_RDONLY) {
- res += "r";
+ if ((mode & O_ACCMODE) == O_RDWR) {
+ res = "rw";
+ } else if ((mode & O_ACCMODE) == O_WRONLY) {
+ res = "w";
+ } else if ((mode & O_ACCMODE) == O_RDONLY) {
+ res = "r";
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1300,12 +1301,12 @@ public class FileUtils {
/** {@hide} */
public static int translateModePosixToPfd(int mode) {
int res = 0;
- if ((mode & O_RDWR) == O_RDWR) {
- res |= MODE_READ_WRITE;
- } else if ((mode & O_WRONLY) == O_WRONLY) {
- res |= MODE_WRITE_ONLY;
- } else if ((mode & O_RDONLY) == O_RDONLY) {
- res |= MODE_READ_ONLY;
+ if ((mode & O_ACCMODE) == O_RDWR) {
+ res = MODE_READ_WRITE;
+ } else if ((mode & O_ACCMODE) == O_WRONLY) {
+ res = MODE_WRITE_ONLY;
+ } else if ((mode & O_ACCMODE) == O_RDONLY) {
+ res = MODE_READ_ONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1325,11 +1326,11 @@ public class FileUtils {
public static int translateModePfdToPosix(int mode) {
int res = 0;
if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
- res |= O_RDWR;
+ res = O_RDWR;
} else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
- res |= O_WRONLY;
+ res = O_WRONLY;
} else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
- res |= O_RDONLY;
+ res = O_RDONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1428,4 +1429,3 @@ public class FileUtils {
}
}
}
-
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 124d7b174739..f3810bddf9c7 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,8 +17,10 @@
package android.os;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.opengl.EGL14;
@@ -57,9 +59,9 @@ public class GraphicsEnvironment {
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
- private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
+ private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -255,8 +257,7 @@ public class GraphicsEnvironment {
return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
}
// Make sure we have good settings to use
- if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
- || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+ if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
Log.w(TAG,
"Global.Settings values are invalid: "
+ "globalSettingsDriverPkgs.size = "
@@ -276,9 +277,141 @@ public class GraphicsEnvironment {
}
/**
+ * Get the ANGLE package name.
+ */
+ private String getAnglePackageName(Context context) {
+ Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
+
+ List<ResolveInfo> resolveInfos = context.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ if (resolveInfos.size() != 1) {
+ Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ + resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ }
+ return "";
+ }
+
+ // Must be exactly 1 ANGLE PKG found to get here.
+ return resolveInfos.get(0).activityInfo.packageName;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a temporary rules file.
+ * True: Temporary rules file was loaded.
+ * False: Temporary rules file was *not* loaded.
+ */
+ private boolean setupAngleWithTempRulesFile(Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Check for temporary rules if debuggable or root
+ if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
+ Log.v(TAG, "Skipping loading temporary rules file");
+ return false;
+ }
+
+ String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+
+ if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+ Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
+ return false;
+ }
+
+ Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+
+ File tempRulesFile = new File(angleTempRules);
+ if (tempRulesFile.exists()) {
+ Log.i(TAG, angleTempRules + " exists, loading file.");
+ try {
+ FileInputStream stream = new FileInputStream(angleTempRules);
+
+ try {
+ FileDescriptor rulesFd = stream.getFD();
+ long rulesOffset = 0;
+ long rulesLength = stream.getChannel().size();
+ Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+
+ setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+
+ stream.close();
+
+ // We successfully setup ANGLE, so return with good status
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
+ }
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Temp ANGLE rules file not found: " + e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+ * True: Rules file was loaded.
+ * False: Rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+ // b/121153494
+ // Skip APK rules file checking.
+ if (!DEBUG) {
+ Log.v(TAG, "Skipping loading the rules file.");
+ // Fill in some default values for now, so the loader can get an answer when it asks.
+ // Most importantly, we need to indicate which app we are init'ing and what the
+ // developer options for it are so we can turn on ANGLE if needed.
+ setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
+ * True: APK rules file was loaded.
+ * False: APK rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesApk(String anglePkgName,
+ ApplicationInfo angleInfo,
+ Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Pass the rules file to loader for ANGLE decisions
+ try {
+ AssetManager angleAssets =
+ context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+
+ try {
+ AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+
+ setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(),
+ assetsFd.getStartOffset(), assetsFd.getLength());
+
+ assetsFd.close();
+
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
+ + " from '" + anglePkgName + "': " + e);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
+ }
+
+ return false;
+ }
+
+ /**
* Pass ANGLE details down to trigger enable logic
*/
- private void setupAngle(Context context, Bundle bundle, String packageName) {
+ public void setupAngle(Context context, Bundle bundle, String packageName) {
String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
@@ -286,98 +419,47 @@ public class GraphicsEnvironment {
+ "set to: '" + devOptIn + "'");
}
+ String anglePkgName = getAnglePackageName(context);
+ if (anglePkgName.isEmpty()) {
+ Log.e(TAG, "Failed to find ANGLE package.");
+ return;
+ }
+
ApplicationInfo angleInfo;
try {
- angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+ angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName,
PackageManager.MATCH_SYSTEM_ONLY);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+ Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
return;
}
String abi = chooseAbi(angleInfo);
// Build a path that includes installed native libs and APK
- StringBuilder sb = new StringBuilder();
- sb.append(angleInfo.nativeLibraryDir)
- .append(File.pathSeparator)
- .append(angleInfo.sourceDir)
- .append("!/lib/")
- .append(abi);
- String paths = sb.toString();
+ String paths = angleInfo.nativeLibraryDir
+ + File.pathSeparator
+ + angleInfo.sourceDir
+ + "!/lib/"
+ + abi;
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
- // Look up rules file to pass to ANGLE
- FileDescriptor rulesFd = null;
- long rulesOffset = 0;
- long rulesLength = 0;
-
- // Check for temporary rules if debuggable or root
- if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
- String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
- if (angleTempRules != null && !angleTempRules.isEmpty()) {
- Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
- File tempRulesFile = new File(angleTempRules);
- if (tempRulesFile.exists()) {
- Log.i(TAG, angleTempRules + " exists, loading file.");
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(angleTempRules);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Unable to create stream for temp ANGLE rules");
- }
-
- if (stream != null) {
- try {
- rulesFd = stream.getFD();
- rulesOffset = 0;
- rulesLength = stream.getChannel().size();
- Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get input stream for " + angleTempRules);
- }
- }
- }
- }
+ if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with a temp rules file, so we're done here.
+ return;
}
- // If no temp rules, load the real ones from the APK
- if (DEBUG && (rulesFd == null)) {
-
- // Pass the rules file to loader for ANGLE decisions
- AssetManager angleAssets = null;
- try {
- angleAssets =
- context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
- return;
- }
-
- AssetFileDescriptor assetsFd = null;
- try {
- assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
- + "'" + ANGLE_PACKAGE_NAME + "'");
- return;
- }
-
- if (assetsFd != null) {
- rulesFd = assetsFd.getFileDescriptor();
- rulesOffset = assetsFd.getStartOffset();
- rulesLength = assetsFd.getLength();
- } else {
- Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
- return;
- }
+ // b/121153494
+ if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+ // We setup ANGLE with defaults, so we're done here.
+ return;
}
- // Further opt-in logic is handled in native, so pass relevant info down
- // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
- // file descriptors open.
- setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+ if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with rules from the APK, so we're done here.
+ return;
+ }
}
/**
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index be8cf0e9137a..fdd74882eb39 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -390,7 +390,7 @@ interface INetworkManagementService
/**
* Setup a new VPN.
*/
- void createVirtualNetwork(int netId, boolean hasDNS, boolean secure);
+ void createVirtualNetwork(int netId, boolean secure);
/**
* Remove a network.
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index fbecc8ec1cd9..f7ffc37f085f 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -16,6 +16,8 @@
package android.os;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.system.ErrnoException;
@@ -108,7 +110,10 @@ public final class NativeHandle implements Closeable {
FileDescriptor[] fds = new FileDescriptor[mFds.length];
try {
for (int i = 0; i < mFds.length; i++) {
- fds[i] = Os.dup(mFds[i]);
+ FileDescriptor newFd = new FileDescriptor();
+ int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
+ newFd.setInt$(fdint);
+ fds[i] = newFd;
}
} catch (ErrnoException e) {
e.rethrowAsIOException();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 6de1ff4bc097..63912ec327a4 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,7 +17,11 @@
package android.os;
import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.F_DUPFD;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+import static android.system.OsConstants.O_CLOEXEC;
import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_CLOEXEC;
import static android.system.OsConstants.SOCK_SEQPACKET;
import static android.system.OsConstants.SOCK_STREAM;
import static android.system.OsConstants.S_IROTH;
@@ -37,6 +41,7 @@ import android.system.StructStat;
import android.util.Log;
import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Memory;
@@ -293,7 +298,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
}
private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
- final int flags = FileUtils.translateModePfdToPosix(mode);
+ final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
int realMode = S_IRWXU | S_IRWXG;
if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
@@ -315,7 +320,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
*/
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
try {
- final FileDescriptor fd = Os.dup(orig);
+ final FileDescriptor fd = new FileDescriptor();
+ int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+ fd.setInt$(intfd);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -351,7 +358,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
original.setInt$(fd);
try {
- final FileDescriptor dup = Os.dup(original);
+ final FileDescriptor dup = new FileDescriptor();
+ int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+ dup.setInt$(intfd);
return new ParcelFileDescriptor(dup);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -413,7 +422,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
*/
public static ParcelFileDescriptor[] createPipe() throws IOException {
try {
- final FileDescriptor[] fds = Os.pipe();
+ final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0]),
new ParcelFileDescriptor(fds[1]) };
@@ -435,7 +444,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
- final FileDescriptor[] fds = Os.pipe();
+ final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0], comm[0]),
new ParcelFileDescriptor(fds[1], comm[1]) };
@@ -459,7 +468,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
try {
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0),
new ParcelFileDescriptor(fd1) };
@@ -489,7 +498,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
final FileDescriptor[] comm = createCommSocketPair();
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0, comm[0]),
new ParcelFileDescriptor(fd1, comm[1]) };
@@ -505,7 +514,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
// across multiple IO operations.
final FileDescriptor comm1 = new FileDescriptor();
final FileDescriptor comm2 = new FileDescriptor();
- Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2);
+ Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2);
IoUtils.setBlocking(comm1, false);
IoUtils.setBlocking(comm2, false);
return new FileDescriptor[] { comm1, comm2 };
@@ -1111,4 +1120,12 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
return "{" + status + ": " + msg + "}";
}
}
+
+ private static boolean isAtLeastQ() {
+ return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
+ }
+
+ private static int ifAtLeastQ(int value) {
+ return isAtLeastQ() ? value : 0;
+ }
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8b1d3c6c839f..9594a713a506 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -132,6 +132,8 @@ public class StorageManager {
public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
/** {@hide} */
public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
+ /** {@hide} */
+ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
/** {@hide} */
public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio";
@@ -1540,7 +1542,9 @@ public class StorageManager {
/** {@hide} */
@TestApi
public static boolean hasIsolatedStorage() {
- return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+ // Prefer to use snapshot for current boot when available
+ return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
+ SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false));
}
/**
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8a03e9eb7507..df1a7131a7ae 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -348,6 +349,32 @@ public final class StorageVolume implements Parcelable {
return intent;
}
+ /**
+ * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+ * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+ * available on the device. The initial location of the document navigation will be the root of
+ * this {@link StorageVolume}.
+ *
+ * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+ * StorageVolume} by default, but the user may select a different location. Callers must respect
+ * the user's chosen location, even if it is different from the originally requested location.
+ *
+ * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+ * of this {@link StorageVolume}
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+ */
+ @NonNull public Intent createOpenDocumentTreeIntent() {
+ final String rootId = isEmulated()
+ ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+ : mFsUuid;
+ final Uri rootUri = DocumentsContract.buildRootUri(
+ DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+ .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+ .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+ return intent;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
index 3ce3c9db2623..c607e3fb309c 100644
--- a/core/java/android/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -16,27 +16,28 @@
package android.permission;
+import static android.permission.RuntimePermissionPresenterService.SERVICE_INTERFACE;
+
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.infra.AbstractRemoteService;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -80,7 +81,7 @@ public final class RuntimePermissionPresenter {
*/
public interface OnCountPermissionAppsResultCallback {
/**
- * The result for {@link #countPermissionApps(List, boolean,
+ * The result for {@link #countPermissionApps(List, boolean, boolean,
* OnCountPermissionAppsResultCallback, Handler)}.
*
* @param numApps The number of apps that have one of the permissions
@@ -110,8 +111,13 @@ public final class RuntimePermissionPresenter {
}
}
- private RuntimePermissionPresenter(Context context) {
- mRemoteService = new RemoteService(context);
+ private RuntimePermissionPresenter(@NonNull Context context) {
+ Intent intent = new Intent(SERVICE_INTERFACE);
+ intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+ ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+
+ mRemoteService = new RemoteService(context,
+ serviceInfo.getComponentInfo().getComponentName());
}
/**
@@ -126,8 +132,8 @@ public final class RuntimePermissionPresenter {
checkNotNull(packageName);
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
- mRemoteService, packageName, callback, handler));
+ mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
+ packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
}
/**
@@ -141,8 +147,8 @@ public final class RuntimePermissionPresenter {
checkNotNull(packageName);
checkNotNull(permissionName);
- mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
- mRemoteService, packageName, permissionName));
+ mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+ permissionName));
}
/**
@@ -160,183 +166,192 @@ public final class RuntimePermissionPresenter {
checkCollectionElementsNotNull(permissionNames, "permissionNames");
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
- mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+ mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
+ permissionNames, countOnlyGranted, countSystem, callback,
+ handler == null ? mRemoteService.getHandler() : handler));
}
- private static final class RemoteService
- extends Handler implements ServiceConnection {
+ /**
+ * A connection to the remote service
+ */
+ static final class RemoteService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteService,
+ IRuntimePermissionPresenter> {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+ private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
- public static final int MSG_UNBIND = 0;
+ /**
+ * Create a connection to the remote service
+ *
+ * @param context A context to use
+ * @param componentName The component of the service to connect to
+ */
+ RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
+ super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
+ service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
+ false, false, 1);
+ }
- private final Object mLock = new Object();
+ /**
+ * @return The default handler used by this service.
+ */
+ Handler getHandler() {
+ return mHandler;
+ }
- private final Context mContext;
+ @Override
+ protected @NonNull IRuntimePermissionPresenter getServiceInterface(
+ @NonNull IBinder binder) {
+ return IRuntimePermissionPresenter.Stub.asInterface(binder);
+ }
- @GuardedBy("mLock")
- private final List<Message> mPendingWork = new ArrayList<>();
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return UNBIND_TIMEOUT_MILLIS;
+ }
- @GuardedBy("mLock")
- private IRuntimePermissionPresenter mRemoteInstance;
+ @Override
+ protected long getRemoteRequestMillis() {
+ return MESSAGE_TIMEOUT_MILLIS;
+ }
- @GuardedBy("mLock")
- private boolean mBound;
+ @Override
+ public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+ IRuntimePermissionPresenter> pendingRequest) {
+ super.scheduleRequest(pendingRequest);
+ }
- RemoteService(Context context) {
- super(context.getMainLooper(), null, false);
- mContext = context;
+ @Override
+ public void scheduleAsyncRequest(
+ @NonNull AsyncRequest<IRuntimePermissionPresenter> request) {
+ super.scheduleAsyncRequest(request);
}
+ }
- public void processMessage(Message message) {
- synchronized (mLock) {
- if (!mBound) {
- Intent intent = new Intent(
- RuntimePermissionPresenterService.SERVICE_INTERFACE);
- intent.setPackage(mContext.getPackageManager()
- .getPermissionControllerPackageName());
- mBound = mContext.bindService(intent, this,
- Context.BIND_AUTO_CREATE);
+ /**
+ * Request for {@link #getAppPermissions}
+ */
+ private static final class PendingGetAppPermissionRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull OnGetAppPermissionResultCallback mCallback;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingGetAppPermissionRequest(@NonNull RemoteService service,
+ @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPackageName = packageName;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
}
- mPendingWork.add(message);
- scheduleNextMessageIfNeededLocked();
- }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+
+ callback.onGetAppPermissions(reportedPermissions);
+
+ finish();
+ }, handler);
}
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
- scheduleNextMessageIfNeededLocked();
- }
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onGetAppPermissions(Collections.emptyList());
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mLock) {
- mRemoteInstance = null;
+ public void run() {
+ try {
+ getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting app permission", e);
}
}
+ }
- private void getAppPermissions(@NonNull String packageName,
- @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.getAppPermissions(packageName,
- new RemoteCallback(result -> {
- final List<RuntimePermissionPresentationInfo> reportedPermissions;
- List<RuntimePermissionPresentationInfo> permissions = null;
- if (result != null) {
- permissions = result.getParcelableArrayList(KEY_RESULT);
- }
- if (permissions == null) {
- permissions = Collections.emptyList();
- }
- reportedPermissions = permissions;
- if (handler != null) {
- handler.post(
- () -> callback.onGetAppPermissions(reportedPermissions));
- } else {
- callback.onGetAppPermissions(reportedPermissions);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- scheduleUnbind();
+ /**
+ * Request for {@link #revokeRuntimePermission}
+ */
+ private static final class PendingRevokeAppPermissionRequest
+ implements AbstractRemoteService.AsyncRequest<IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull String mPermissionName;
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
- }
+ private PendingRevokeAppPermissionRequest(@NonNull String packageName,
+ @NonNull String permissionName) {
+ mPackageName = packageName;
+ mPermissionName = permissionName;
}
- private void revokeAppPermissions(@NonNull String packageName,
- @NonNull String permissionName) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
+ @Override
+ public void run(IRuntimePermissionPresenter remoteInterface) {
try {
- remoteInstance.revokeRuntimePermission(packageName, permissionName);
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
+ remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error revoking app permission", e);
}
}
+ }
- private void countPermissionApps(@NonNull List<String> permissionNames,
- boolean countOnlyGranted, boolean countSystem,
- @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
-
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
-
- try {
- remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
- new RemoteCallback(result -> {
- final int numApps;
- if (result != null) {
- numApps = result.getInt(KEY_RESULT);
- } else {
- numApps = 0;
- }
-
- if (handler != null) {
- handler.post(() -> callback.onCountPermissionApps(numApps));
- } else {
- callback.onCountPermissionApps(numApps);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error counting permission apps", re);
- }
+ /**
+ * Request for {@link #countPermissionApps}
+ */
+ private static final class PendingCountPermissionAppsRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull List<String> mPermissionNames;
+ private final @NonNull OnCountPermissionAppsResultCallback mCallback;
+ private final boolean mCountOnlyGranted;
+ private final boolean mCountSystem;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
+ @NonNull List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, @NonNull OnCountPermissionAppsResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPermissionNames = permissionNames;
+ mCountOnlyGranted = countOnlyGranted;
+ mCountSystem = countSystem;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final int numApps;
+ if (result != null) {
+ numApps = result.getInt(KEY_RESULT);
+ } else {
+ numApps = 0;
+ }
- scheduleUnbind();
+ callback.onCountPermissionApps(numApps);
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
- }
+ finish();
+ }, handler);
}
- private void unbind() {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
- }
- mRemoteInstance = null;
- }
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onCountPermissionApps(0);
}
- @GuardedBy("mLock")
- private void scheduleNextMessageIfNeededLocked() {
- if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
- Message nextMessage = mPendingWork.remove(0);
- sendMessage(nextMessage);
+ @Override
+ public void run() {
+ try {
+ getService().getServiceInterface().countPermissionApps(mPermissionNames,
+ mCountOnlyGranted, mCountSystem, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error counting permission apps", e);
}
}
-
- private void scheduleUnbind() {
- removeMessages(MSG_UNBIND);
- sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
- .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
- }
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cd991cc0d6fc..a323ed1a51cb 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -238,6 +238,9 @@ public final class DocumentsContract {
"com.android.externalstorage.documents";
/** {@hide} */
+ public static final String EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary";
+
+ /** {@hide} */
public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
/**
@@ -857,16 +860,6 @@ public final class DocumentsContract {
}
/**
- * Builds URI for user home directory on external (local) storage.
- * {@hide}
- */
- public static Uri buildHomeUri() {
- // TODO: Avoid this type of interpackage copying. Added here to avoid
- // direct coupling, but not ideal.
- return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
- }
-
- /**
* Build URI representing the recently modified documents of a specific root
* in a document provider. When queried, a provider will return zero or more
* rows with columns defined by {@link Document}.
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f38f74046934..457453fd210b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1002,6 +1002,7 @@ public final class MediaStore {
public static final int MICRO_KIND = 3;
public static final Point MINI_SIZE = new Point(512, 384);
+ public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
public static final Point MICRO_SIZE = new Point(96, 96);
}
@@ -1127,6 +1128,8 @@ public final class MediaStore {
final Point size;
if (kind == ThumbnailConstants.MICRO_KIND) {
size = ThumbnailConstants.MICRO_SIZE;
+ } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+ size = ThumbnailConstants.FULL_SCREEN_SIZE;
} else if (kind == ThumbnailConstants.MINI_KIND) {
size = ThumbnailConstants.MINI_SIZE;
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 299db730b3c6..d93985c8ec3d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12820,6 +12820,17 @@ public final class Settings {
"privileged_device_identifier_3p_check_relaxed";
/**
+ * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+ * permission check for preloaded non-privileged apps.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_non_priv_check_relaxed";
+
+ /**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
* and restoring to lower version of platform API will be skipped.
*
@@ -12949,14 +12960,20 @@ public final class Settings {
/**
* Property used by {@code com.android.server.SystemServer} on start to decide whether
- * the Content Capture service should be created or not
+ * the Content Capture service should be created or not.
*
- * <p>By default it should *NOT* be set (in which case the decision is based on whether
- * the OEM provides an implementation for the service), but it can be overridden to:
+ * <p>Possible values are:
*
* <ul>
- * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
- * <li>Enable the CTS tests to be run on AOSP builds
+ * <li>If set to {@code default}, it will only be set if the OEM provides and defines the
+ * service name by overlaying {@code config_defaultContentCaptureService} (this is the
+ * "default" mode)
+ * <li>If set to {@code always}, it will always be enabled, even when the resource is not
+ * overlaid (this is useful during development and to run the CTS tests on AOSP builds).
+ * <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
+ * can disable it remotely in case of emergency by setting to something else (like
+ * {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
+ * of the values above).
* </ul>
*
* @hide
@@ -14260,6 +14277,44 @@ public final class Settings {
}
}
+ /**
+ * <p>
+ * A Settings panel is floating UI that contains a fixed subset of settings to address a
+ * particular user problem. For example, the
+ * {@link #ACTION_INTERNET_CONNECTIVITY Internet Panel} surfaces settings related to
+ * connecting to the internet.
+ * <p>
+ * Settings panels appear above the calling app to address the problem without
+ * the user needing to open Settings and thus leave their current screen.
+ */
+ public static final class Panel {
+ private Panel() {
+ }
+
+ /**
+ * Activity Action: Show a settings dialog containing settings to enable internet
+ * connection.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_INTERNET_CONNECTIVITY =
+ "android.settings.panel.action.INTERNET_CONNECTIVITY";
+
+ /**
+ * Activity Action: Show a settings dialog containing all volume streams.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VOLUME =
+ "android.settings.panel.action.VOLUME";
+ }
+
private static final String[] PM_WRITE_SETTINGS = {
android.Manifest.permission.WRITE_SETTINGS
};
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index df58f52dc59c..784e71993815 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -21,21 +21,26 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureEvent;
+import java.util.Arrays;
import java.util.List;
/**
- * Batch of content capture events.
+ * Not needed anymore...
+ *
+ * @deprecated
*
* @hide
*/
@SystemApi
+@Deprecated
public final class ContentCaptureEventsRequest implements Parcelable {
+// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
- private final List<ContentCaptureEvent> mEvents;
+ private final ContentCaptureEvent mEvent;
/** @hide */
- public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
- mEvents = events;
+ public ContentCaptureEventsRequest(@NonNull ContentCaptureEvent event) {
+ mEvent = event;
}
/**
@@ -43,7 +48,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
*/
@NonNull
public List<ContentCaptureEvent> getEvents() {
- return mEvents;
+ return Arrays.asList(mEvent);
}
@Override
@@ -53,7 +58,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedList(mEvents, flags);
+ parcel.writeParcelable(mEvent, flags);
}
public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
@@ -61,8 +66,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
@Override
public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
- return new ContentCaptureEventsRequest(parcel
- .createTypedArrayList(ContentCaptureEvent.CREATOR));
+ return new ContentCaptureEventsRequest(parcel.readParcelable(null));
}
@Override
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 58848fce43d6..c5e62f1dfe60 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -24,15 +24,28 @@ import android.annotation.SystemApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
+import android.view.contentcapture.ActivityContentCaptureSession;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.IContentCaptureDirectManager;
+import com.android.internal.os.IResultReceiver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Set;
@@ -63,39 +76,53 @@ public abstract class ContentCaptureService extends Service {
private Handler mHandler;
- private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
+ /**
+ * Binder that receives calls from the system server.
+ */
+ private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
@Override
- public void onSessionLifecycle(ContentCaptureContext context, String sessionId)
- throws RemoteException {
- if (context != null) {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnCreateSession,
- ContentCaptureService.this, context, sessionId));
- } else {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnDestroySession,
- ContentCaptureService.this, sessionId));
- }
+ public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
+ IResultReceiver clientReceiver) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
+ ContentCaptureService.this, context, sessionId, uid, clientReceiver));
}
@Override
- public void onContentCaptureEventsRequest(String sessionId,
- ContentCaptureEventsRequest request) {
+ public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
- ContentCaptureService.this, sessionId, request));
+ obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
+ ContentCaptureService.this, sessionId, snapshotData));
+ }
+ @Override
+ public void onSessionFinished(String sessionId) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
+ ContentCaptureService.this, sessionId));
}
+ };
+
+ /**
+ * Binder that receives calls from the app.
+ */
+ private final IContentCaptureDirectManager mClientInterface =
+ new IContentCaptureDirectManager.Stub() {
@Override
- public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
- ContentCaptureService.this, sessionId, snapshotData));
+ public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
+ ContentCaptureService.this, Binder.getCallingUid(), events));
}
};
+ /**
+ * List of sessions per UID.
+ *
+ * <p>This map is populated when an session is started, which is called by the system server
+ * and can be trusted. Then subsequent calls made by the app are verified against this map.
+ */
+ private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+
@CallSuper
@Override
public void onCreate() {
@@ -107,7 +134,7 @@ public abstract class ContentCaptureService extends Service {
@Override
public final IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
+ return mServerInterface.asBinder();
}
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
@@ -190,15 +217,28 @@ public abstract class ContentCaptureService extends Service {
}
/**
+ *
+ * @deprecated use {@link #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)}
+ * instead.
+ */
+ @Deprecated
+ public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
+ @NonNull ContentCaptureEventsRequest request) {
+ if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+ }
+
+ /**
* Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
* session.
*
* @param sessionId the session's Id
- * @param request the events
+ * @param event the event
*/
- public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
- @NonNull ContentCaptureEventsRequest request);
-
+ public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
+ @NonNull ContentCaptureEvent event) {
+ if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+ onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event));
+ }
/**
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
*
@@ -212,10 +252,22 @@ public abstract class ContentCaptureService extends Service {
* Destroys the content capture session.
*
* @param sessionId the id of the session to destroy
- */
+ * */
public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
- if (VERBOSE) {
- Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+ if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+ }
+
+ @Override
+ @CallSuper
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final int size = mSessionsByUid.size();
+ pw.print("Number sessions: "); pw.println(size);
+ if (size > 0) {
+ final String prefix = " ";
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
+ pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+ }
}
}
@@ -223,13 +275,32 @@ public abstract class ContentCaptureService extends Service {
// so we don't need to create a temporary InteractionSessionId for each event.
private void handleOnCreateSession(@NonNull ContentCaptureContext context,
- @NonNull String sessionId) {
+ @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
+ mSessionsByUid.put(sessionId, uid);
onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
+ setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
+ mClientInterface.asBinder());
}
- private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
- @NonNull ContentCaptureEventsRequest request) {
- onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request);
+ private void handleSendEvents(int uid,
+ @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+
+ // Most events belong to the same session, so we can keep a reference to the last one
+ // to avoid creating too many ContentCaptureSessionId objects
+ String lastSessionId = null;
+ ContentCaptureSessionId sessionId = null;
+
+ final List<ContentCaptureEvent> events = parceledEvents.getList();
+ for (int i = 0; i < events.size(); i++) {
+ final ContentCaptureEvent event = events.get(i);
+ String sessionIdString = event.getSessionId();
+ if (!sessionIdString.equals(lastSessionId)) {
+ if (!handleIsRightCallerFor(sessionIdString, uid)) continue;
+ sessionId = new ContentCaptureSessionId(sessionIdString);
+ lastSessionId = sessionIdString;
+ }
+ onContentCaptureEvent(sessionId, event);
+ }
}
private void handleOnActivitySnapshot(@NonNull String sessionId,
@@ -237,7 +308,52 @@ public abstract class ContentCaptureService extends Service {
onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
}
- private void handleOnDestroySession(@NonNull String sessionId) {
+ private void handleFinishSession(@NonNull String sessionId) {
+ mSessionsByUid.remove(sessionId);
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
}
+
+ /**
+ * Checks if the given {@code uid} owns the session.
+ */
+ private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
+ final Integer rightUid = mSessionsByUid.get(sessionId);
+ if (rightUid == null) {
+ if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
+ // Just ignore, as the session could have finished
+ return false;
+ }
+ if (rightUid != uid) {
+ Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
+ + rightUid);
+ //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * Sends the state of the {@link ContentCaptureManager} in the cleint app.
+ *
+ * @param clientReceiver receiver in the client app.
+ * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
+ * service.
+ * @hide
+ */
+ public static void setClientState(@NonNull IResultReceiver clientReceiver,
+ int sessionStatus, @Nullable IBinder binder) {
+ try {
+ final Bundle extras;
+ if (binder != null) {
+ extras = new Bundle();
+ extras.putBinder(ActivityContentCaptureSession.EXTRA_BINDER, binder);
+ } else {
+ extras = null;
+ }
+ clientReceiver.send(sessionStatus, extras);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
}
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 8167be9e6838..20e8e99d4ce1 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,10 +16,11 @@
package android.service.contentcapture;
-import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.SnapshotData;
import android.view.contentcapture.ContentCaptureContext;
+import com.android.internal.os.IResultReceiver;
+
import java.util.List;
/**
@@ -28,11 +29,8 @@ import java.util.List;
* @hide
*/
oneway interface IContentCaptureService {
-
- // Called when session is created (context not null) or destroyed (context null)
- void onSessionLifecycle(in ContentCaptureContext context, String sessionId);
-
- void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
-
+ void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
+ in IResultReceiver clientReceiver);
+ void onSessionFinished(String sessionId);
void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4b81a7260852..6b569cfa9722 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,6 +15,7 @@
*/
package android.service.quicksettings;
+import android.annotation.Nullable;
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.Parcel;
@@ -62,6 +63,7 @@ public final class Tile implements Parcelable {
private IBinder mToken;
private Icon mIcon;
private CharSequence mLabel;
+ private CharSequence mSubtitle;
private CharSequence mContentDescription;
// Default to active until clients of the new API can update.
private int mState = STATE_ACTIVE;
@@ -152,6 +154,22 @@ public final class Tile implements Parcelable {
}
/**
+ * Gets the current subtitle for the tile.
+ */
+ @Nullable
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ /**
+ * Set the subtitle for the tile. Will be displayed as the secondary label.
+ * @param subtitle the subtitle to show.
+ */
+ public void setSubtitle(@Nullable CharSequence subtitle) {
+ this.mSubtitle = subtitle;
+ }
+
+ /**
* Gets the current content description for the tile.
*/
public CharSequence getContentDescription() {
@@ -195,6 +213,7 @@ public final class Tile implements Parcelable {
}
dest.writeInt(mState);
TextUtils.writeToParcel(mLabel, dest, flags);
+ TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
}
@@ -206,6 +225,7 @@ public final class Tile implements Parcelable {
}
mState = source.readInt();
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a095b0d8b239..f295b705e4e6 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -25,6 +25,7 @@ import android.app.Service;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -52,12 +53,12 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -211,7 +212,8 @@ public abstract class WallpaperService extends Service {
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
- Display mDisplay;
+ private Display mDisplay;
+ private Context mDisplayContext;
private int mDisplayState;
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
@@ -1038,6 +1040,7 @@ public abstract class WallpaperService extends Service {
mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
mCaller.getHandler());
mDisplay = mIWallpaperEngine.mDisplay;
+ mDisplayContext = createDisplayContext(mDisplay);
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1050,6 +1053,18 @@ public abstract class WallpaperService extends Service {
}
/**
+ * The {@link Context} with resources that match the current display the wallpaper is on.
+ * For multiple display environment, multiple engines can be created to render on each
+ * display, but these displays may have different densities. Use this context to get the
+ * corresponding resources for currently display, avoiding the context of the service.
+ *
+ * @return A {@link Context} for current display.
+ */
+ public Context getDisplayContext() {
+ return mDisplayContext;
+ }
+
+ /**
* Executes life cycle event and updates internal ambient mode state based on
* message sent from handler.
*
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ade7577dc666..8b97e0e4a107 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -46,6 +46,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+ DEFAULT_FLAGS.put("settings_slice_injection", "false");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index ada78532d63f..60eeeeaa77ff 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -201,22 +201,16 @@ public class NotificationHeaderView extends ViewGroup {
int bottom = top + childHeight;
int layoutLeft = left;
int layoutRight = right;
- if (child == mExpandButton && mShowExpandButtonAtEnd) {
- layoutRight = end - mContentEndMargin;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
- }
- if (child == mProfileBadge) {
- int paddingEnd = getPaddingEnd();
- if (mShowWorkBadgeAtEnd) {
- paddingEnd = mContentEndMargin;
+ if ((child == mExpandButton && mShowExpandButtonAtEnd)
+ || child == mProfileBadge
+ || child == mAppOps) {
+ if (end == getMeasuredWidth()) {
+ layoutRight = end - mContentEndMargin;
+ } else {
+ layoutRight = end - params.getMarginEnd();
}
- layoutRight = end - paddingEnd;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
- }
- if (child == mAppOps) {
- int paddingEnd = mContentEndMargin;
- layoutRight = end - paddingEnd;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ layoutLeft = layoutRight - child.getMeasuredWidth();
+ end = layoutLeft - params.getMarginStart();
}
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
int ltrLeft = layoutLeft;
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index bef9f07afb5f..2ea95e90a8bf 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -157,6 +157,11 @@ public class TouchDelegate {
* Forward hover events to the delegate view if the event is within the bounds
* specified in the constructor and touch exploration is enabled.
*
+ * <p>This method is provided for accessibility purposes so touch exploration, which is
+ * commonly used by screen readers, can properly place accessibility focus on views that
+ * use touch delegates. Therefore, touch exploration must be enabled for hover events
+ * to be dispatched through the delegate.</p>
+ *
* @param event The hover event to forward
* @return True if the event was consumed by the delegate, false otherwise.
*
diff --git a/core/java/android/view/contentcapture/ActivityContentCaptureSession.java b/core/java/android/view/contentcapture/ActivityContentCaptureSession.java
new file mode 100644
index 000000000000..a8f3e0bbd45d
--- /dev/null
+++ b/core/java/android/view/contentcapture/ActivityContentCaptureSession.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureManager.DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.TimeUtils;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This session is created when the activity starts and finished when it stops; clients can use
+ * it to create children activities.
+ *
+ * <p><b>NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread. Hence, the only field that must be thread-safe is {@code mEnabled}, which is called at
+ * the beginning of every method.
+ *
+ * @hide
+ */
+public final class ActivityContentCaptureSession extends ContentCaptureSession {
+
+ /**
+ * Handler message used to flush the buffer.
+ */
+ private static final int MSG_FLUSH = 1;
+
+ /**
+ * Maximum number of events that are buffered before sent to the app.
+ */
+ // TODO(b/121044064): use settings
+ private static final int MAX_BUFFER_SIZE = 100;
+
+ /**
+ * Frequency the buffer is flushed if stale.
+ */
+ // TODO(b/121044064): use settings
+ private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
+ @NonNull
+ private final AtomicBoolean mDisabled;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * Interface to the system_server binder object - it's only used to start the session (and
+ * notify when the session is finished).
+ */
+ @Nullable
+ private final IContentCaptureManager mSystemServerInterface;
+
+ /**
+ * Direct interface to the service binder object - it's used to send the events, including the
+ * last ones (when the session is finished)
+ */
+ @Nullable
+ private IContentCaptureDirectManager mDirectServiceInterface;
+ @Nullable
+ private DeathRecipient mDirectServiceVulture;
+
+ private int mState = STATE_UNKNOWN;
+
+ @Nullable
+ private IBinder mApplicationToken;
+
+ @Nullable
+ private ComponentName mComponentName;
+
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @Nullable
+ private ArrayList<ContentCaptureEvent> mEvents;
+
+ // Used just for debugging purposes (on dump)
+ private long mNextFlush;
+
+ // Lazily created on demand.
+ private ContentCaptureSessionId mContentCaptureSessionId;
+
+ /**
+ * @hide */
+ protected ActivityContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
+ @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
+ @Nullable ContentCaptureContext clientContext) {
+ super(clientContext);
+ mContext = context;
+ mHandler = handler;
+ mSystemServerInterface = systemServerInterface;
+ mDisabled = disabled;
+ }
+
+ /**
+ * Starts this session.
+ *
+ * @hide
+ */
+ void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
+ if (!isContentCaptureEnabled()) return;
+
+ if (VERBOSE) {
+ Log.v(mTag, "start(): token=" + applicationToken + ", comp="
+ + ComponentName.flattenToShortString(activityComponent));
+ }
+
+ mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleStartSession, this,
+ applicationToken, activityComponent));
+ }
+
+ @Override
+ void flush() {
+ mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleForceFlush, this));
+ }
+
+ @Override
+ void onDestroy() {
+ mHandler.sendMessage(
+ obtainMessage(ActivityContentCaptureSession::handleDestroySession, this));
+ }
+
+ private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
+ Log.w(mTag, "ignoring handleStartSession(" + token + ") while on state "
+ + getStateAsString(mState));
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(mTag, "handleStartSession(): token=" + token + ", act="
+ + getActivityDebugName() + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+ componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ IBinder binder = null;
+ if (resultData != null) {
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(mTag, "No " + EXTRA_BINDER + " extra result");
+ handleResetState();
+ return;
+ }
+ }
+ handleSessionStarted(resultCode, binder);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error starting session for " + componentName.flattenToShortString() + ": "
+ + e);
+ }
+ }
+
+ /**
+ * Callback from {@code system_server} after call to
+ * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+ * ContentCaptureContext, int, IResultReceiver)}.
+ *
+ * @param resultCode session state
+ * @param binder handle to {@code IContentCaptureDirectManager}
+ */
+ private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
+ mState = resultCode;
+ if (binder != null) {
+ mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+ mDirectServiceVulture = () -> {
+ Log.w(mTag, "Destroying session " + mId + " because service died");
+ destroy();
+ };
+ try {
+ binder.linkToDeath(mDirectServiceVulture, 0);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Failed to link to death on " + binder + ": " + e);
+ }
+ }
+ if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ mDisabled.set(true);
+ handleResetSession(/* resetState= */ false);
+ } else {
+ mDisabled.set(false);
+ }
+ if (VERBOSE) {
+ Log.v(mTag, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+ + ", binder=" + binder);
+ }
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ if (mEvents == null) {
+ if (VERBOSE) {
+ Log.v(mTag, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+ }
+ mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+ }
+ mEvents.add(event);
+
+ final int numberEvents = mEvents.size();
+
+ // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
+ // buffered (either total or per autofillid). For
+ // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
+ // "a" and "b" then send "abc".
+ final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+ if (bufferEvent && !forceFlush) {
+ handleScheduleFlush(/* checkExisting= */ true);
+ return;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(mTag, "Closing session for " + getActivityDebugName()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
+ handleResetState();
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+
+ handleForceFlush();
+ }
+
+ private void handleScheduleFlush(boolean checkExisting) {
+ if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
+ // "Renew" the flush message by removing the previous one
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+ mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+ if (VERBOSE) {
+ Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+ }
+ mHandler.sendMessageDelayed(
+ obtainMessage(ActivityContentCaptureSession::handleFlushIfNeeded, this)
+ .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
+ }
+
+ private void handleFlushIfNeeded() {
+ if (mEvents.isEmpty()) {
+ if (VERBOSE) Log.v(mTag, "Nothing to flush");
+ return;
+ }
+ handleForceFlush();
+ }
+
+ private void handleForceFlush() {
+ if (mEvents == null) return;
+
+ if (mDirectServiceInterface == null) {
+ Log.w(mTag, "handleForceFlush(): client not available yet");
+ if (!mHandler.hasMessages(MSG_FLUSH)) {
+ handleScheduleFlush(/* checkExisting= */ false);
+ }
+ return;
+ }
+
+ final int numberEvents = mEvents.size();
+ try {
+ if (DEBUG) {
+ Log.d(mTag, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+ }
+ mHandler.removeMessages(MSG_FLUSH);
+
+ final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+ mDirectServiceInterface.sendEvents(events);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ + ": " + e);
+ }
+ }
+
+ /**
+ * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+ */
+ @NonNull
+ private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+ // NOTE: we must save a reference to the current mEvents and then set it to to null,
+ // otherwise clearing it would clear it in the receiving side if the service is also local.
+ final List<ContentCaptureEvent> events = mEvents == null
+ ? Collections.emptyList()
+ : mEvents;
+ mEvents = null;
+ return new ParceledListSlice<>(events);
+ }
+
+ private void handleDestroySession() {
+ if (DEBUG) {
+ Log.d(mTag, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
+ }
+
+ try {
+ mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error destroying system-service session " + mId + " for "
+ + getActivityDebugName() + ": " + e);
+ }
+ }
+
+ private void handleResetState() {
+ handleResetSession(/* resetState= */ true);
+ }
+
+ // TODO(b/121042846): once we support multiple sessions, we might need to move some of these
+ // clearings out.
+ private void handleResetSession(boolean resetState) {
+ if (resetState) {
+ mState = STATE_UNKNOWN;
+ }
+ mContentCaptureSessionId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ mEvents = null;
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+
+ @Override
+ void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+ mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(mId, TYPE_VIEW_APPEARED)
+ .setViewNode(node.mNode), /* forceFlush= */ false));
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+ mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(mId, TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+ /* forceFlush= */ false));
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(mId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text), /* forceFlush= */ false));
+ }
+
+ @Override
+ boolean isContentCaptureEnabled() {
+ return mSystemServerInterface != null && !mDisabled.get();
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("id: "); pw.println(mId);
+ pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mSystemServerInterface != null) {
+ pw.print(prefix); pw.print("mSystemServerInterface: ");
+ pw.println(mSystemServerInterface);
+ }
+ if (mDirectServiceInterface != null) {
+ pw.print(prefix); pw.print("mDirectServiceInterface: ");
+ pw.println(mDirectServiceInterface);
+ }
+ if (mClientContext != null) {
+ // NOTE: we don't dump clientContent because it could have PII
+ pw.print(prefix); pw.println("hasClientContext");
+
+ }
+ pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ if (mContentCaptureSessionId != null) {
+ pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
+ }
+ pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsString(mState)); pw.println(")");
+ if (mApplicationToken != null) {
+ pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null && !mEvents.isEmpty()) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+ if (VERBOSE && numberEvents > 0) {
+ final String prefix3 = prefix + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+ pw.print(prefix); pw.print("next flush: ");
+ TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
+ }
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityDebugName() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
+ }
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 5d8fe5f51464..43c9699b54b8 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -29,7 +29,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-// TODO(b/111276913): add javadocs / implement Parcelable / implement
/** @hide */
@SystemApi
public final class ContentCaptureEvent implements Parcelable {
@@ -72,6 +71,7 @@ public final class ContentCaptureEvent implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
+ private final @NonNull String mSessionId;
private final int mType;
private final long mEventTime;
private final int mFlags;
@@ -80,21 +80,21 @@ public final class ContentCaptureEvent implements Parcelable {
private @Nullable CharSequence mText;
/** @hide */
- public ContentCaptureEvent(int type, long eventTime, int flags) {
+ public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+ mSessionId = sessionId;
mType = type;
mEventTime = eventTime;
mFlags = flags;
}
-
/** @hide */
- public ContentCaptureEvent(int type, int flags) {
- this(type, System.currentTimeMillis(), flags);
+ public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
+ this(sessionId, type, System.currentTimeMillis(), flags);
}
/** @hide */
- public ContentCaptureEvent(int type) {
- this(type, /* flags= */ 0);
+ public ContentCaptureEvent(@NonNull String sessionId, int type) {
+ this(sessionId, type, /* flags= */ 0);
}
/** @hide */
@@ -104,12 +104,20 @@ public final class ContentCaptureEvent implements Parcelable {
}
/** @hide */
+ @NonNull
+ public String getSessionId() {
+ return mSessionId;
+ }
+
+ /** @hide */
+ @NonNull
public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
mNode = Preconditions.checkNotNull(node);
return this;
}
/** @hide */
+ @NonNull
public ContentCaptureEvent setText(@Nullable CharSequence text) {
mText = text;
return this;
@@ -214,6 +222,7 @@ public final class ContentCaptureEvent implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mSessionId);
parcel.writeInt(mType);
parcel.writeLong(mEventTime);
parcel.writeInt(mFlags);
@@ -227,10 +236,12 @@ public final class ContentCaptureEvent implements Parcelable {
@Override
public ContentCaptureEvent createFromParcel(Parcel parcel) {
+ final String sessionId = parcel.readString();
final int type = parcel.readInt();
final long eventTime = parcel.readLong();
final int flags = parcel.readInt();
- final ContentCaptureEvent event = new ContentCaptureEvent(type, eventTime, flags);
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(sessionId, type, eventTime, flags);
final AutofillId id = parcel.readParcelable(null);
if (id != null) {
event.setAutofillId(id);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 7fbbfb775397..fca2857d6e06 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -66,7 +66,7 @@ public final class ContentCaptureManager {
@NonNull
private final Handler mHandler;
- private ContentCaptureSession mMainSession;
+ private ActivityContentCaptureSession mMainSession;
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@@ -110,7 +110,7 @@ public final class ContentCaptureManager {
// 4.Close (and delete) these sessions when onActivityStopped() is called.
// 5.Figure out whether each session will have its own mDisabled AtomicBoolean.
if (mMainSession == null) {
- mMainSession = new ContentCaptureSession(mContext, mHandler, mService,
+ mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService,
mDisabled, Preconditions.checkNotNull(context));
} else {
throw new IllegalStateException("Manager already has a session: " + mMainSession);
@@ -127,12 +127,12 @@ public final class ContentCaptureManager {
* @hide
*/
@NonNull
- public ContentCaptureSession getMainContentCaptureSession() {
+ public ActivityContentCaptureSession getMainContentCaptureSession() {
// TODO(b/121033016): figure out how to manage the "default" session when it support
// multiple sessions (can't just be the first one, as it could be closed).
if (mMainSession == null) {
- mMainSession = new ContentCaptureSession(mContext, mHandler, mService, mDisabled,
- /* contentCaptureContext= */ null);
+ mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService,
+ mDisabled, /* clientContext= */ null);
if (VERBOSE) {
Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 632955d335cf..aedb7a94ff5d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,58 +15,32 @@
*/
package android.view.contentcapture;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-import static android.view.contentcapture.ContentCaptureManager.DEBUG;
import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
-import android.util.TimeUtils;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Session used to notify a system-provided Content Capture service about events associated with
* views.
*/
-public final class ContentCaptureSession implements AutoCloseable {
-
- /*
- * IMPLEMENTATION NOTICE:
- *
- * All methods in this class should return right away, or do the real work in a handler thread.
- *
- * Hence, the only field that must be thread-safe is mEnabled, which is called at the
- * beginning of every method.
- */
-
- private static final String TAG = ContentCaptureSession.class.getSimpleName();
+public abstract class ContentCaptureSession implements AutoCloseable {
/**
* Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
+ *
* thext change was caused by user input (for example, through IME).
*/
public static final int FLAG_USER_INPUT = 0x1;
@@ -100,58 +74,23 @@ public final class ContentCaptureSession implements AutoCloseable {
public static final int STATE_DISABLED = 3;
/**
- * Handler message used to flush the buffer.
- */
- private static final int MSG_FLUSH = 1;
-
- /**
- * Maximum number of events that are buffered before sent to the app.
+ * Session is disabled because its id already existed on server.
+ *
+ * @hide
*/
- // TODO(b/121044064): use settings
- private static final int MAX_BUFFER_SIZE = 100;
+ public static final int STATE_DISABLED_DUPLICATED_ID = 4;
- /**
- * Frequency the buffer is flushed if stale.
- */
- // TODO(b/121044064): use settings
- private static final int FLUSHING_FREQUENCY_MS = 5_000;
+ /** @hide */
+ protected final String mTag = getClass().getSimpleName();
private final CloseGuard mCloseGuard = CloseGuard.get();
- @NonNull
- private final AtomicBoolean mDisabled;
-
- @NonNull
- private final Context mContext;
-
- @NonNull
- private final Handler mHandler;
-
- @Nullable
- private final IContentCaptureManager mService;
-
+ /** @hide */
@Nullable
- private final String mId = UUID.randomUUID().toString();
+ protected final String mId = UUID.randomUUID().toString();
private int mState = STATE_UNKNOWN;
- @Nullable
- private IBinder mApplicationToken;
-
- @Nullable
- private ComponentName mComponentName;
-
- /**
- * List of events held to be sent as a batch.
- */
- // TODO(b/111276913): once we support multiple sessions, we need to move the buffer of events
- // to its own class so it's shared by all sessions
- @Nullable
- private ArrayList<ContentCaptureEvent> mEvents;
-
- // Used just for debugging purposes (on dump)
- private long mNextFlush;
-
// Lazily created on demand.
private ContentCaptureSessionId mContentCaptureSessionId;
@@ -159,18 +98,15 @@ public final class ContentCaptureSession implements AutoCloseable {
* {@link ContentCaptureContext} set by client, or {@code null} when it's the
* {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
* context.
+ *
+ * @hide
*/
@Nullable
- private final ContentCaptureContext mClientContext;
+ // TODO(b/121042846): move to ChildContentCaptureSession.java
+ protected final ContentCaptureContext mClientContext;
/** @hide */
- protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
- @Nullable IContentCaptureManager service, @NonNull AtomicBoolean disabled,
- @Nullable ContentCaptureContext clientContext) {
- mContext = context;
- mHandler = handler;
- mService = service;
- mDisabled = disabled;
+ protected ContentCaptureSession(@Nullable ContentCaptureContext clientContext) {
mClientContext = clientContext;
mCloseGuard.open("destroy");
}
@@ -178,7 +114,7 @@ public final class ContentCaptureSession implements AutoCloseable {
/**
* Gets the id used to identify this session.
*/
- public ContentCaptureSessionId getContentCaptureSessionId() {
+ public final ContentCaptureSessionId getContentCaptureSessionId() {
if (mContentCaptureSessionId == null) {
mContentCaptureSessionId = new ContentCaptureSessionId(mId);
}
@@ -186,37 +122,16 @@ public final class ContentCaptureSession implements AutoCloseable {
}
/**
- * Starts this session.
- *
- * @hide
- */
- void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
- if (!isContentCaptureEnabled()) return;
-
- if (VERBOSE) {
- Log.v(TAG, "start(): token=" + applicationToken + ", comp="
- + ComponentName.flattenToShortString(activityComponent));
- }
-
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleStartSession, this,
- applicationToken, activityComponent));
- }
-
- /**
* Flushes the buffered events to the service.
- *
- * @hide
*/
- void flush() {
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleForceFlush, this));
- }
+ abstract void flush();
/**
* Destroys this session, flushing out all pending notifications to the service.
*
* <p>Once destroyed, any new notification will be dropped.
*/
- public void destroy() {
+ public final void destroy() {
//TODO(b/111276913): mark it as destroyed so other methods are ignored (and test on CTS)
if (!isContentCaptureEnabled()) return;
@@ -224,13 +139,19 @@ public final class ContentCaptureSession implements AutoCloseable {
//TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
// id) and send it to the cache of batched commands
if (VERBOSE) {
- Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+ Log.v(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
}
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
+ flush();
+
+ onDestroy();
+
mCloseGuard.close();
}
+ abstract void onDestroy();
+
+
/** @hide */
@Override
public void close() {
@@ -249,162 +170,6 @@ public final class ContentCaptureSession implements AutoCloseable {
}
}
- private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
- if (mState != STATE_UNKNOWN) {
- // TODO(b/111276913): revisit this scenario
- Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
- + getStateAsString(mState));
- return;
- }
- mState = STATE_WAITING_FOR_SERVER;
- mApplicationToken = token;
- mComponentName = componentName;
-
- if (VERBOSE) {
- Log.v(TAG, "handleStartSession(): token=" + token + ", act="
- + getActivityDebugName() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
-
- try {
- mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
- mId, mClientContext, flags, new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) {
- handleSessionStarted(resultCode);
- }
- });
- } catch (RemoteException e) {
- Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
- + e);
- }
- }
-
- private void handleSessionStarted(int resultCode) {
- mState = resultCode;
- mDisabled.set(mState == STATE_DISABLED);
- if (VERBOSE) {
- Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
- + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
- }
- }
-
- private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
- if (mEvents == null) {
- if (VERBOSE) {
- Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
- }
- mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
- }
- mEvents.add(event);
-
- final int numberEvents = mEvents.size();
-
- // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
- // buffered (either total or per autofillid). For
- // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
- // "a" and "b" then send "abc".
- final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
-
- if (bufferEvent && !forceFlush) {
- handleScheduleFlush();
- return;
- }
-
- if (mState != STATE_ACTIVE) {
- // Callback from startSession hasn't been called yet - typically happens on system
- // apps that are started before the system service
- // TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead. Similarly, the manager service should return right away
- // when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugName()
- + " after " + numberEvents + " delayed events and state "
- + getStateAsString(mState));
- }
- handleResetState();
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
- // when it's launched again
- return;
- }
-
- handleForceFlush();
- }
-
- private void handleScheduleFlush() {
- if (mHandler.hasMessages(MSG_FLUSH)) {
- // "Renew" the flush message by removing the previous one
- mHandler.removeMessages(MSG_FLUSH);
- }
- mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
- if (VERBOSE) {
- Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
- }
- mHandler.sendMessageDelayed(
- obtainMessage(ContentCaptureSession::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
- FLUSHING_FREQUENCY_MS);
- }
-
- private void handleFlushIfNeeded() {
- if (mEvents.isEmpty()) {
- if (VERBOSE) Log.v(TAG, "Nothing to flush");
- return;
- }
- handleForceFlush();
- }
-
- private void handleForceFlush() {
- if (mEvents == null) return;
-
- final int numberEvents = mEvents.size();
- try {
- if (DEBUG) {
- Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
- }
- mHandler.removeMessages(MSG_FLUSH);
- mService.sendEvents(mContext.getUserId(), mId, mEvents);
- // TODO(b/111276913): decide whether we should clear or set it to null, as each has
- // its own advantages: clearing will save extra allocations while the session is
- // active, while setting to null would save memory if there's no more event coming.
- mEvents.clear();
- } catch (RemoteException e) {
- Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
- + ": " + e);
- }
- }
-
- private void handleDestroySession() {
- //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
- // to system_server, so it's ok to call both in sequence here. But once we split
- // them so the events are sent directly to the service, we need to make sure they're
- // sent in order.
- try {
- if (DEBUG) {
- Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
- + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
- + getActivityDebugName());
- }
-
- mService.finishSession(mContext.getUserId(), mId, mEvents);
- } catch (RemoteException e) {
- Log.e(TAG, "Error destroying session " + mId + " for " + getActivityDebugName()
- + ": " + e);
- } finally {
- handleResetState();
- }
- }
-
- // TODO(b/111276913): once we support multiple sessions, we might need to move some of these
- // clearings out.
- private void handleResetState() {
- mState = STATE_UNKNOWN;
- mContentCaptureSessionId = null;
- mApplicationToken = null;
- mComponentName = null;
- mEvents = null;
- mHandler.removeMessages(MSG_FLUSH);
- }
-
/**
* Notifies the Content Capture Service that a node has been added to the view structure.
*
@@ -414,7 +179,7 @@ public final class ContentCaptureSession implements AutoCloseable {
*
* @param node node that has been added.
*/
- public void notifyViewAppeared(@NonNull ViewStructure node) {
+ public final void notifyViewAppeared(@NonNull ViewStructure node) {
Preconditions.checkNotNull(node);
if (!isContentCaptureEnabled()) return;
@@ -422,12 +187,11 @@ public final class ContentCaptureSession implements AutoCloseable {
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
- /* forceFlush= */ false));
+ internalNotifyViewAppeared((ViewStructureImpl) node);
}
+ abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+
/**
* Notifies the Content Capture Service that a node has been removed from the view structure.
*
@@ -436,15 +200,15 @@ public final class ContentCaptureSession implements AutoCloseable {
*
* @param id id of the node that has been removed.
*/
- public void notifyViewDisappeared(@NonNull AutofillId id) {
+ public final void notifyViewDisappeared(@NonNull AutofillId id) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
- /* forceFlush= */ false));
+ internalNotifyViewDisappeared(id);
}
+ abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+
/**
* Notifies the Intelligence Service that the value of a text node has been changed.
*
@@ -453,24 +217,25 @@ public final class ContentCaptureSession implements AutoCloseable {
* @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
* changed by the user (for example, through the keyboard).
*/
- public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
int flags) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
- .setText(text), /* forceFlush= */ false));
+ internalNotifyViewTextChanged(id, text, flags);
}
+ abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
* @hide
*/
@NonNull
- public ViewStructure newViewStructure(@NonNull View view) {
+ public final ViewStructure newViewStructure(@NonNull View view) {
return new ViewNode.ViewStructureImpl(view);
}
@@ -487,73 +252,25 @@ public final class ContentCaptureSession implements AutoCloseable {
* @hide
*/
@NonNull
- public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
+ int virtualId) {
return new ViewNode.ViewStructureImpl(parentId, virtualId);
}
- private boolean isContentCaptureEnabled() {
- return mService != null && !mDisabled.get();
- }
-
- void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("id: "); pw.println(mId);
- pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
- pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
- if (mService != null) {
- pw.print(prefix); pw.print("mService: "); pw.println(mService);
- }
- if (mClientContext != null) {
- // NOTE: we don't dump clientContent because it could have PII
- pw.print(prefix); pw.println("hasClientContext");
+ abstract boolean isContentCaptureEnabled();
- }
- pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
- pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
- if (mContentCaptureSessionId != null) {
- pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
- }
- pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsString(mState)); pw.println(")");
- if (mApplicationToken != null) {
- pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
- }
- if (mComponentName != null) {
- pw.print(prefix); pw.print("component name: ");
- pw.println(mComponentName.flattenToShortString());
- }
- if (mEvents != null && !mEvents.isEmpty()) {
- final int numberEvents = mEvents.size();
- pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
- pw.print('/'); pw.println(MAX_BUFFER_SIZE);
- if (VERBOSE && numberEvents > 0) {
- final String prefix3 = prefix + " ";
- for (int i = 0; i < numberEvents; i++) {
- final ContentCaptureEvent event = mEvents.get(i);
- pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
- pw.println();
- }
- }
- pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
- pw.print(prefix); pw.print("next flush: ");
- TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
- }
- }
-
- /**
- * Gets a string that can be used to identify the activity on logging statements.
- */
- private String getActivityDebugName() {
- return mComponentName == null ? mContext.getPackageName()
- : mComponentName.flattenToShortString();
- }
+ abstract void dump(@NonNull String prefix, @NonNull PrintWriter pw);
@Override
public String toString() {
return mId;
}
+ /**
+ * @hide
+ */
@NonNull
- private static String getStateAsString(int state) {
+ protected static String getStateAsString(int state) {
switch (state) {
case STATE_UNKNOWN:
return "UNKNOWN";
@@ -563,6 +280,8 @@ public final class ContentCaptureSession implements AutoCloseable {
return "ACTIVE";
case STATE_DISABLED:
return "DISABLED";
+ case STATE_DISABLED_DUPLICATED_ID:
+ return "DISABLED_DUPLICATED_ID";
default:
return "INVALID:" + state;
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
new file mode 100644
index 000000000000..8d8117bf9ca1
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.pm.ParceledListSlice;
+import android.view.contentcapture.ContentCaptureEvent;
+
+/**
+ * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
+ * the ContentCaptureService implementation.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureDirectManager {
+ void sendEvents(in ParceledListSlice events);
+}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 2002c5c1fc9a..cbd37017038e 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -26,12 +26,14 @@ import com.android.internal.os.IResultReceiver;
import java.util.List;
/**
- * {@hide}
- */
+ * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server
+ * implementation service (ContentCaptureManagerService).
+ *
+ * @hide
+ */
oneway interface IContentCaptureManager {
void startSession(int userId, IBinder activityToken, in ComponentName componentName,
String sessionId, in ContentCaptureContext clientContext, int flags,
in IResultReceiver result);
- void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
- void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
+ void finishSession(int userId, String sessionId);
}
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 3fa1b01cf701..e44d6ebf37d1 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -26,6 +26,7 @@ import android.content.Context;
abstract class WebSyncManager implements Runnable {
protected static final java.lang.String LOGTAG = "websync";
protected android.webkit.WebViewDatabase mDataBase;
+ @UnsupportedAppUsage
protected android.os.Handler mHandler;
protected WebSyncManager(Context context, String name) {
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index 213bb75e6c6c..c093fe512186 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -37,12 +37,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.time.DateTimeException;
-import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
/**
* Controller for managing night display and color mode settings.
@@ -152,28 +148,6 @@ public final class ColorDisplayController {
}
/**
- * Returns the time when Night display's activation state last changed, or {@code null} if it
- * has never been changed.
- */
- public LocalDateTime getLastActivatedTime() {
- final ContentResolver cr = mContext.getContentResolver();
- final String lastActivatedTime = Secure.getStringForUser(
- cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
- if (lastActivatedTime != null) {
- try {
- return LocalDateTime.parse(lastActivatedTime);
- } catch (DateTimeParseException ignored) {}
- // Uses the old epoch time.
- try {
- return LocalDateTime.ofInstant(
- Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
- ZoneId.systemDefault());
- } catch (DateTimeException|NumberFormatException ignored) {}
- }
- return null;
- }
-
- /**
* Returns the current auto mode value controlling when Night display will be automatically
* activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
* {@link #AUTO_MODE_TWILIGHT}.
diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
new file mode 100644
index 000000000000..fa5c30a03e78
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+// Iterface to observe op note/checks of ops
+oneway interface IAppOpsNotedCallback {
+ void opNoted(int op, int uid, String packageName, int mode);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 049103bfebb2..e59bee42c21c 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -21,6 +21,7 @@ import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
interface IAppOpsService {
// These first methods are also called by native code, so must
@@ -61,4 +62,9 @@ interface IAppOpsService {
boolean isOperationActive(int code, int uid, String packageName);
void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
+
+ void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
+ void stopWatchingNoted(IAppOpsNotedCallback callback);
+
+ int checkOperationRaw(int code, int uid, String packageName);
}
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 4da339165655..2df515835026 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -17,6 +17,7 @@
package com.android.internal.app.procstats;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -192,9 +193,16 @@ public final class AssociationState {
*/
String mProcess;
- SourceKey(int uid, String process) {
+ /**
+ * Optional package name, or null; consider this final. Not final just to avoid a
+ * temporary object during lookup.
+ */
+ @Nullable String mPackage;
+
+ SourceKey(int uid, String process, String pkg) {
mUid = uid;
mProcess = process;
+ mPackage = pkg;
}
public boolean equals(Object o) {
@@ -202,12 +210,14 @@ public final class AssociationState {
return false;
}
SourceKey s = (SourceKey) o;
- return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+ return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
+ && Objects.equals(s.mPackage, mPackage);
}
@Override
public int hashCode() {
- return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+ return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
+ ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
}
@Override
@@ -217,6 +227,8 @@ public final class AssociationState {
UserHandle.formatUid(sb, mUid);
sb.append(' ');
sb.append(mProcess);
+ sb.append(' ');
+ sb.append(mPackage);
sb.append('}');
return sb.toString();
}
@@ -227,7 +239,7 @@ public final class AssociationState {
*/
private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
- private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+ private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
private ProcessState mProc;
@@ -266,12 +278,13 @@ public final class AssociationState {
mProc = proc;
}
- public SourceState startSource(int uid, String processName) {
+ public SourceState startSource(int uid, String processName, String packageName) {
mTmpSourceKey.mUid = uid;
mTmpSourceKey.mProcess = processName;
+ mTmpSourceKey.mPackage = packageName;
SourceState src = mSources.get(mTmpSourceKey);
if (src == null) {
- SourceKey key = new SourceKey(uid, processName);
+ SourceKey key = new SourceKey(uid, processName, packageName);
src = new SourceState(key);
mSources.put(key, src);
}
@@ -379,6 +392,7 @@ public final class AssociationState {
final SourceState src = mSources.valueAt(isrc);
out.writeInt(key.mUid);
stats.writeCommonString(out, key.mProcess);
+ stats.writeCommonString(out, key.mPackage);
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
@@ -405,7 +419,8 @@ public final class AssociationState {
for (int isrc = 0; isrc < NSRC; isrc++) {
final int uid = in.readInt();
final String procName = stats.readCommonString(in, parcelVersion);
- final SourceKey key = new SourceKey(uid, procName);
+ final String pkgName = stats.readCommonString(in, parcelVersion);
+ final SourceKey key = new SourceKey(uid, procName, pkgName);
final SourceState src = new SourceState(key);
src.mCount = in.readInt();
src.mDuration = in.readLong();
@@ -445,10 +460,11 @@ public final class AssociationState {
}
}
- public boolean hasProcess(String procName) {
+ public boolean hasProcessOrPackage(String procName) {
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
- if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+ final SourceKey key = mSources.keyAt(isrc);
+ if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
return true;
}
}
@@ -466,14 +482,20 @@ public final class AssociationState {
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
- if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+ if (reqPackage != null && !reqPackage.equals(key.mProcess)
+ && !reqPackage.equals(key.mPackage)) {
continue;
}
pw.print(prefixInner);
pw.print("<- ");
pw.print(key.mProcess);
- pw.print(" / ");
+ pw.print("/");
UserHandle.formatUid(pw, key.mUid);
+ if (key.mPackage != null) {
+ pw.print(" (");
+ pw.print(key.mPackage);
+ pw.print(")");
+ }
pw.println(":");
pw.print(prefixInner);
pw.print(" Total count ");
@@ -683,6 +705,7 @@ public final class AssociationState {
final SourceState src = mSources.valueAt(isrc);
final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
+ proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
long duration = src.mDuration;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9ee583a97b8d..9b9b77196a53 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -178,7 +178,7 @@ public final class ProcessStats implements Parcelable {
{"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 34;
+ private static final int PARCEL_VERSION = 35;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -1490,7 +1490,7 @@ public final class ProcessStats implements Parcelable {
// package, so that if so we print those.
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
- if (asc.hasProcess(reqPackage)) {
+ if (asc.hasProcessOrPackage(reqPackage)) {
onlyAssociations = true;
break;
}
@@ -1591,7 +1591,7 @@ public final class ProcessStats implements Parcelable {
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
- if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+ if (!onlyAssociations || !asc.hasProcessOrPackage(reqPackage)) {
continue;
}
}
diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index aaea45e4adbf..26cf1809313e 100644
--- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 41dcf89a0b04..33918400d571 100644
--- a/services/core/java/com/android/server/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,13 +28,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.FgThread;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -109,7 +110,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
mComponentName = componentName;
mIntent = new Intent(serviceInterface).setComponent(mComponentName);
mUserId = userId;
- mHandler = new Handler(FgThread.getHandler().getLooper());
+ mHandler = new Handler(Looper.getMainLooper());
mBindInstantServiceAllowed = bindInstantServiceAllowed;
}
@@ -164,6 +165,15 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
*/
protected abstract long getRemoteRequestMillis();
+ /**
+ * Gets the currently registered service interface or {@code null} if the service is not
+ * connected.
+ */
+ @Nullable
+ public final I getServiceInterface() {
+ return mService;
+ }
+
private void handleDestroy() {
if (checkIfDestroyed()) return;
handleOnDestroy();
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index d32f13b5d71b..f0c223388137 100644
--- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8bdb000aad0e..c2c6ae6712ab 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -536,9 +536,11 @@ public class ZygoteInit {
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
+ // We use the boot class loader, that's what the runtime expects at AOT.
+ ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
+
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
- ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
- null /* classLoaderName */);
+ parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
}
/**
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 5118e5f0473e..9087dd219d97 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,7 +67,8 @@ interface IStatusBarService
in NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
- void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+ boolean generatedByAsssistant);
void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index cac22652ebd7..240c2e757faf 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -16,12 +16,12 @@
package com.android.internal.usb;
-import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DFP;
-import static android.hardware.usb.UsbPort.MODE_DUAL;
-import static android.hardware.usb.UsbPort.MODE_NONE;
-import static android.hardware.usb.UsbPort.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index 27c583457a41..d2156704a655 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -53,6 +54,7 @@ public class NumericTextView extends TextView {
private OnValueChangedListener mListener;
+ @UnsupportedAppUsage
public NumericTextView(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 97247aada49d..a65214a1edca 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@ public class BaseNetdEventCallback extends INetdEventCallback.Stub {
}
@Override
+ public void onNat64PrefixEvent(int netId, boolean added, String prefixString,
+ int prefixLength) {
+ // default no-op
+ }
+
+ @Override
public void onPrivateDnsValidationEvent(int netId, String ipAddress,
String hostname, boolean validated) {
// default no-op
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43f8d00bb5f1..dc6a73a7d75c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -271,9 +271,10 @@ cc_library_shared {
"libhardware",
"libhardware_legacy",
"libselinux",
- "libicuuc",
+ "libandroidicu",
"libmedia",
"libmediametrics",
+ "libmeminfo",
"libaudioclient",
"libjpeg",
"libusbhost",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c32de0a5737e..12ca78a7ce92 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -725,9 +725,10 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
return NULL;
}
- // Map the pixels in place and take ownership of the ashmem region.
- nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
- dupFd, const_cast<void*>(blob.data()), size, !isMutable));
+ // Map the pixels in place and take ownership of the ashmem region. We must also respect the
+ // rowBytes value already set on the bitmap instead of attempting to compute our own.
+ nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
+ const_cast<void*>(blob.data()), size, !isMutable);
if (!nativeBitmap) {
close(dupFd);
blob.release();
@@ -1097,21 +1098,20 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit
SkBitmap src;
hwuiBitmap.getSkBitmap(&src);
- SkBitmap result;
- HeapAllocator allocator;
- if (!bitmapCopyTo(&result, hwuiBitmap.info().colorType(), src, &allocator)) {
+ if (src.pixelRef() == nullptr) {
doThrowRE(env, "Could not copy a hardware bitmap.");
return NULL;
}
- return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false));
+
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef());
+ return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
}
static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
- // Bitmap::createFrom currently assumes SRGB color space for RGBA images.
// To support any color space, we need to pass an additional ColorSpace argument to
// java Bitmap.createHardwareBitmap.
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB());
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from graphic buffer");
return NULL;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 26af15e79e2d..67d0c8aced61 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,36 +424,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
///////////////////////////////////////////////////////////////////////////////
-android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
- int fd, void* addr, size_t size, bool readOnly) {
- const SkImageInfo& info = bitmap->info();
- if (info.colorType() == kUnknown_SkColorType) {
- doThrowIAE(env, "unknown bitmap configuration");
- return nullptr;
- }
-
- if (!addr) {
- // Map existing ashmem region if not already mapped.
- int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
- size = ashmem_get_size_region(fd);
- addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED) {
- return nullptr;
- }
- }
-
- // we must respect the rowBytes value already set on the bitmap instead of
- // attempting to compute our own.
- const size_t rowBytes = bitmap->rowBytes();
-
- auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes);
- wrapper->getSkBitmap(bitmap);
- if (readOnly) {
- bitmap->pixelRef()->setImmutable();
- }
- return wrapper;
-}
-
SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
SkColorSpaceTransferFn p;
p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cee3c46dd67f..b0bd68336e08 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -85,9 +85,6 @@ public:
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
- static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
- int fd, void* addr, size_t size, bool readOnly);
-
/**
* Given a bitmap we natively allocate a memory block to store the contents
* of that bitmap. The memory is then attached to the bitmap via an
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 84f53468e941..fc9ea76f6ed3 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -102,6 +102,10 @@ static jint saveLayerAlpha(jlong canvasHandle, jfloat l, jfloat t,
return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
}
+static jint saveUnclippedLayer(jlong canvasHandle, jint l, jint t, jint r, jint b) {
+ return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
static bool restore(jlong canvasHandle) {
Canvas* canvas = get_canvas(canvasHandle);
if (canvas->getSaveCount() <= 1) {
@@ -651,6 +655,7 @@ static const JNINativeMethod gMethods[] = {
{"nSave","(JI)I", (void*) CanvasJNI::save},
{"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
{"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
{"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
{"nRestore","(J)Z", (void*) CanvasJNI::restore},
{"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 283eb035c6f7..29d8f30543e6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -32,6 +32,7 @@
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include "android_media_AudioEffectDescriptor.h"
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_MicrophoneInfo.h"
@@ -427,9 +428,14 @@ android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val)
}
static void
-android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
- audio_patch_handle_t patchHandle)
+android_media_AudioSystem_recording_callback(int event,
+ const record_client_info_t *clientInfo,
+ const audio_config_base_t *clientConfig,
+ std::vector<effect_descriptor_t> clientEffects,
+ const audio_config_base_t *deviceConfig,
+ std::vector<effect_descriptor_t> effects __unused,
+ audio_patch_handle_t patchHandle,
+ audio_source_t source)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
@@ -460,14 +466,24 @@ android_media_AudioSystem_recording_callback(int event, const record_client_info
recParamData[6] = (jint) patchHandle;
env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
+ jobjectArray jClientEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jClientEffects, clientEffects);
+
+ jobjectArray jEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jEffects, effects);
+
// callback into java
jclass clazz = env->FindClass(kClassPathName);
+
env->CallStaticVoidMethod(clazz,
- gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
- event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
+ gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+ event, (jint) clientInfo->uid, clientInfo->session,
+ clientInfo->source, clientInfo->port_id, clientInfo->silenced,
+ recParamArray, jClientEffects, jEffects, source);
env->DeleteLocalRef(clazz);
-
env->DeleteLocalRef(recParamArray);
+ env->DeleteLocalRef(jClientEffects);
+ env->DeleteLocalRef(jEffects);
}
static jint
@@ -2260,7 +2276,7 @@ int register_android_media_AudioSystem(JNIEnv *env)
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
- "recordingCallbackFromNative", "(IIII[I)V");
+ "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fa1da4bfbf3a..888dab19c247 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -32,6 +32,7 @@
#include <atomic>
#include <iomanip>
#include <string>
+#include <vector>
#include <debuggerd/client.h>
#include <log/log.h>
@@ -41,6 +42,7 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
+#include <meminfo/sysmeminfo.h>
#include <memtrack/memtrack.h>
#include <memunreachable/memunreachable.h>
#include "android_os_Debug.h"
@@ -712,6 +714,8 @@ static long get_allocated_vmalloc_memory() {
return vmalloc_allocated_size;
}
+// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
+// Debug.java.
enum {
MEMINFO_TOTAL,
MEMINFO_FREE,
@@ -731,138 +735,43 @@ enum {
MEMINFO_COUNT
};
-static long long get_zram_mem_used()
-{
-#define ZRAM_SYSFS "/sys/block/zram0/"
- UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
- if (mm_stat_file) {
- long long mem_used_total = 0;
-
- int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
- if (matched != 1)
- ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
-
- return mem_used_total;
- }
-
- UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
- if (mem_used_total_file) {
- long long mem_used_total = 0;
-
- int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
- if (matched != 1)
- ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
-
- return mem_used_total;
- }
-
- return 0;
-}
-
static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
{
- char buffer[4096];
- size_t numFound = 0;
-
if (out == NULL) {
jniThrowNullPointerException(env, "out == null");
return;
}
- int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
- if (fd < 0) {
- ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
+ int outLen = env->GetArrayLength(out);
+ if (outLen < MEMINFO_COUNT) {
+ jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT");
return;
}
- int len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0) {
- ALOGW("Empty /proc/meminfo");
+ // Read system memory info including ZRAM. The values are stored in the vector
+ // in the same order as MEMINFO_* enum
+ std::vector<uint64_t> mem(MEMINFO_COUNT);
+ std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags);
+ tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:");
+ ::android::meminfo::SysMemInfo smi;
+ if (!smi.ReadMemInfo(tags, &mem)) {
+ jniThrowRuntimeException(env, "SysMemInfo read failed");
return;
}
- buffer[len] = 0;
-
- static const char* const tags[] = {
- "MemTotal:",
- "MemFree:",
- "Buffers:",
- "Cached:",
- "Shmem:",
- "Slab:",
- "SReclaimable:",
- "SUnreclaim:",
- "SwapTotal:",
- "SwapFree:",
- "ZRam:",
- "Mapped:",
- "VmallocUsed:",
- "PageTables:",
- "KernelStack:",
- NULL
- };
- static const int tagsLen[] = {
- 9,
- 8,
- 8,
- 7,
- 6,
- 5,
- 13,
- 11,
- 10,
- 9,
- 5,
- 7,
- 12,
- 11,
- 12,
- 0
- };
- long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- char* p = buffer;
- while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
- int i = 0;
- while (tags[i]) {
- if (strncmp(p, tags[i], tagsLen[i]) == 0) {
- p += tagsLen[i];
- while (*p == ' ') p++;
- char* num = p;
- while (*p >= '0' && *p <= '9') p++;
- if (*p != 0) {
- *p = 0;
- p++;
- }
- mem[i] = atoll(num);
- numFound++;
- break;
- }
- i++;
- }
- while (*p && *p != '\n') {
- p++;
- }
- if (*p) p++;
- }
-
- mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
- // Recompute Vmalloc Used since the value in meminfo
- // doesn't account for I/O remapping which doesn't use RAM.
- mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
- int maxNum = env->GetArrayLength(out);
- if (maxNum > MEMINFO_COUNT) {
- maxNum = MEMINFO_COUNT;
- }
jlong* outArray = env->GetLongArrayElements(out, 0);
if (outArray != NULL) {
- for (int i=0; i<maxNum; i++) {
+ outLen = MEMINFO_COUNT;
+ for (int i = 0; i < outLen; i++) {
+ // TODO: move get_allocated_vmalloc_memory() to libmeminfo
+ if (i == MEMINFO_VMALLOC_USED) {
+ outArray[i] = get_allocated_vmalloc_memory() / 1024;
+ continue;
+ }
outArray[i] = mem[i];
}
}
+
env->ReleaseLongArrayElements(out, outArray, 0);
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 102a0b7b8957..0c1a8aa18370 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -25,8 +25,12 @@
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <meminfo/sysmeminfo.h>
#include <processgroup/processgroup.h>
+#include <string>
+#include <vector>
+
#include "core_jni_helpers.h"
#include "android_util_Binder.h"
@@ -39,9 +43,11 @@
#include <inttypes.h>
#include <pwd.h>
#include <signal.h>
+#include <string.h>
#include <sys/errno.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
@@ -603,66 +609,34 @@ static int pid_compare(const void* v1, const void* v2)
return *((const jint*)v1) - *((const jint*)v2);
}
-static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
+static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
- int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
+ static const std::vector<std::string> memFreeTags = {
+ ::android::meminfo::SysMemInfo::kMemFree,
+ ::android::meminfo::SysMemInfo::kMemCached,
+ };
+ std::vector<uint64_t> mem(memFreeTags.size());
+ ::android::meminfo::SysMemInfo smi;
- if (fd < 0) {
- ALOGW("Unable to open /proc/meminfo");
- return -1;
+ if (!smi.ReadMemInfo(memFreeTags, &mem)) {
+ jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
+ return -1L;
}
- char buffer[2048];
- const int len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0) {
- ALOGW("Unable to read /proc/meminfo");
- return -1;
- }
- buffer[len] = 0;
-
- size_t numFound = 0;
- jlong mem = 0;
-
- char* p = buffer;
- while (*p && numFound < num) {
- int i = 0;
- while (sums[i]) {
- if (strncmp(p, sums[i], sumsLen[i]) == 0) {
- p += sumsLen[i];
- while (*p == ' ') p++;
- char* num = p;
- while (*p >= '0' && *p <= '9') p++;
- if (*p != 0) {
- *p = 0;
- p++;
- if (*p == 0) p--;
- }
- mem += atoll(num) * 1024;
- numFound++;
- break;
- }
- i++;
- }
- p++;
- }
-
- return numFound > 0 ? mem : -1;
-}
-
-static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
-{
- static const char* const sums[] = { "MemFree:", "Cached:", NULL };
- static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
- return getFreeMemoryImpl(sums, sumsLen, 2);
+ jlong sum = 0;
+ std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
+ return sum * 1024;
}
static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
- static const char* const sums[] = { "MemTotal:", NULL };
- static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
- return getFreeMemoryImpl(sums, sumsLen, 1);
+ struct sysinfo si;
+ if (sysinfo(&si) == -1) {
+ ALOGE("sysinfo failed: %s", strerror(errno));
+ return -1;
+ }
+
+ return si.totalram;
}
void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index e0fd1a634557..40a133b9383b 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -168,6 +168,11 @@ static void android_view_DisplayListCanvas_drawCircleProps(jlong canvasPtr,
canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
}
+static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
+ Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+ canvas->drawWebViewFunctor(functor);
+}
+
// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -192,6 +197,7 @@ static JNINativeMethod gMethods[] = {
{ "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer },
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
{ "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
+ { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
};
int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 702741eb813c..5a8ab3c1bdc4 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -31,8 +31,6 @@
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
#include <private/EGL/cache.h>
#include <utils/Looper.h>
@@ -58,6 +56,7 @@
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
#include <pipeline/skia/ShaderCache.h>
+#include <utils/Color.h>
namespace android {
@@ -1011,10 +1010,9 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(
buffer->getWidth(), buffer->getHeight(), width, height);
// Continue I guess?
}
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
- // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
- // format and SRGB color space.
- // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
+
+ sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
return bitmap::createBitmap(env, bitmap.release(),
android::bitmap::kBitmapCreateFlag_Premultiplied);
}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index ce19ce3519b2..71ebcc1e3659 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -186,7 +186,7 @@ message PackageServiceStatsProto {
repeated PackageServiceOperationStatsProto operation_stats = 2;
}
-// Next Tag: 7
+// Next Tag: 8
message PackageAssociationSourceProcessStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -194,6 +194,8 @@ message PackageAssociationSourceProcessStatsProto {
optional int32 process_uid = 1;
// Process name.
optional string process_name = 2;
+ // Package name.
+ optional string package_name = 7;
// Total count of the times this association appeared.
optional int32 total_count = 3;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7fa3e66bae25..8b6f33fd97e7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1642,6 +1642,12 @@
<permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
android:protectionLevel="signature" />
+ <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
+ when the device is stationary in order to save power.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
+ android:protectionLevel="signature|privileged" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
@@ -1997,6 +2003,15 @@
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.telecom.CallRedirectionService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 5ba1cf259551..0f53549a966c 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -111,6 +111,7 @@
android:background="@null"
android:layout_width="@dimen/notification_header_expand_icon_size"
android:layout_height="@dimen/notification_header_expand_icon_size"
+ android:layout_marginStart="4dp"
android:paddingTop="@dimen/notification_expand_button_padding_top"
android:visibility="gone"
android:contentDescription="@string/expand_button_content_description_collapsed"
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
index 0ad2bdcbb52d..a071927f46b2 100644
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ b/core/res/res/values-night/themes_permission_controller.xml
@@ -28,8 +28,5 @@
<style name="Theme.DeviceDefault.PermissionGrant"
parent="@style/Theme.DeviceDefault.Dialog">
<item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
- <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
- <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
- <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
</style>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9277daec7027..9f5eab557026 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1165,6 +1165,10 @@
<!-- The default suggested battery % at which we enable battery saver automatically. -->
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
+ <!-- The app which will handle routine based automatic battery saver, if empty the UI for
+ routine based battery saver will be hidden -->
+ <string name="config_batterySaverScheduleProvider"></string>
+
<!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
plus this -->
<integer name="config_lowBatteryCloseWarningBump">5</integer>
@@ -2611,6 +2615,11 @@
<!-- Package name for default network scorer app; overridden by product overlays. -->
<string name="config_defaultNetworkScorerPackageName"></string>
+ <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized
+ for low memory devices and replace the app transition starting window with the splash
+ screen. -->
+ <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
+
<!-- Determines whether recent tasks are provided to the user. Default device has recents
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
@@ -3488,8 +3497,6 @@
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false"></string>
- <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
- <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
<!-- Allows setting custom fontFeatureSettings on specific text. -->
<string name="config_headlineFontFeatureSettings" translatable="false"></string>
@@ -3556,8 +3563,6 @@
<string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">sans-serif</string>
- <!-- Name of a font family to use for light body text. -->
- <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
<!-- Name of a font family to use for medium body text. -->
<string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
@@ -3642,4 +3647,11 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+
+ <!-- This is the default launcher component to use on secondary displays that support system
+ decorations.
+ This launcher activity must support multiple instances and have corresponding launch mode
+ set in AndroidManifest.
+ {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
+ <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6f75d90c46c4..799d9d858b59 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2978,6 +2978,11 @@
<public name="config_mediaMetadataBitmapMaxSize" />
</public-group>
+ <public-group type="color" first-id="0x0106001c">
+ <!-- @hide @SystemApi -->
+ <public name="system_notification_accent_color" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
index e6e0de3a6f30..5a9d3e6c2889 100644
--- a/core/res/res/values/styles_permission_controller.xml
+++ b/core/res/res/values/styles_permission_controller.xml
@@ -25,15 +25,16 @@
</style>
<style name="PermissionGrantTitleIcon">
- <item name="layout_width">36dp</item>
- <item name="layout_height">36dp</item>
+ <item name="layout_width">24dp</item>
+ <item name="layout_height">24dp</item>
+ <item name="layout_marginBottom">12dp</item>
<item name="tint">?attr/colorAccent</item>
<item name="scaleType">fitCenter</item>
</style>
<style name="PermissionGrantTitleMessage"
parent="@style/TextAppearance.DeviceDefault">
- <item name="paddingStart">22dp</item>
+ <item name="gravity">center</item>
<item name="textSize">20sp</item>
<item name="textColor">?attr/textColorPrimary</item>
</style>
@@ -46,56 +47,22 @@
</style>
<style name="PermissionGrantDescription">
- <item name="layout_marginTop">20dp</item>
<item name="layout_marginStart">24dp</item>
- <item name="layout_marginBottom">16dp</item>
<item name="layout_marginEnd">24dp</item>
</style>
<style name="PermissionGrantContent">
- <item name="layout_marginStart">16dp</item>
+ <item name="layout_marginStart">24dp</item>
<item name="layout_marginEnd">24dp</item>
</style>
- <style name="PermissionGrantRadioGroup">
- <item name="layout_marginStart">8dp</item>
- <item name="layout_marginTop">-4dp</item>
- </style>
-
- <style name="PermissionGrantRadioButton"
- parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
- <item name="paddingStart">16dp</item>
- <item name="paddingTop">8dp</item>
- <item name="paddingBottom">8dp</item>
- <item name="textSize">16sp</item>
- </style>
-
<style name="PermissionGrantDetailMessage"
parent="@style/TextAppearance.DeviceDefault">
- <item name="layout_marginStart">8dp</item>
- <item name="layout_marginBottom">4dp</item>
+ <item name="layout_marginTop">18dp</item>
<item name="textColor">?attr/textColorPrimary</item>
<item name="textSize">16sp</item>
</style>
- <style name="PermissionGrantDetailMessageSpace">
- <item name="layout_height">8dp</item>
- </style>
-
- <style name="PermissionGrantCheckbox"
- parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
- <item name="paddingStart">16dp</item>
- <item name="textColor">?attr/textColorSecondary</item>
- <item name="buttonTint">?attr/textColorSecondary</item>
- <item name="textSize">16sp</item>
- </style>
-
- <style name="PermissionGrantButtonBar">
- <item name="layout_marginStart">24dp</item>
- <item name="layout_marginEnd">16dp</item>
- <item name="layout_marginBottom">4dp</item>
- </style>
-
<!-- styles for the permission review screen. -->
<style name="PermissionReviewDescription">
<item name="layout_marginTop">20dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d68681d8c51d..4ed050117541 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
/* Copyright 2012, The Android Open Source Project
**
@@ -330,6 +329,7 @@
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+ <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
@@ -3328,7 +3328,6 @@
<java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
- <java-symbol type="string" name="config_headlineFontFamilyLight" />
<java-symbol type="string" name="config_headlineFontFamilyMedium" />
<java-symbol type="drawable" name="stat_sys_vitals" />
@@ -3463,6 +3462,7 @@
<java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
+ <java-symbol type="string" name="config_batterySaverScheduleProvider" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
@@ -3521,4 +3521,7 @@
<java-symbol type="dimen" name="rounded_corner_radius_bottom" />
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
+
+ <!-- For Secondary Launcher -->
+ <java-symbol type="string" name="config_secondaryHomeComponent" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0ed821200ed4..56265cc04c0c 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1442,7 +1442,7 @@ easier.
<style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
<!-- action bar -->
<item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
- <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
@@ -1678,11 +1678,8 @@ easier.
<style name="ThemeOverlay.DeviceDefault" />
- <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
- primary text -->
- <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
- <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
- </style>
+ <!-- @hide Theme overlay that inherits from material actionbar -->
+ <style name="ThemeOverlay.DeviceDefault.ActionBar" parent="ThemeOverlay.Material.ActionBar" />
<!-- @hide Theme overlay for a light popup in action bar -->
<style name="ThemeOverlay.DeviceDefault.Popup.Light" parent="@style/ThemeOverlay.Material.Light" />
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
index 369cee3d98f1..205c4eb2d9a2 100644
--- a/core/res/res/values/themes_permission_controller.xml
+++ b/core/res/res/values/themes_permission_controller.xml
@@ -28,9 +28,6 @@
<style name="Theme.DeviceDefault.PermissionGrant"
parent="@style/Theme.DeviceDefault.Light.Dialog">
<item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
- <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
- <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
- <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
</style>
<!-- themes for the permission review dialog. -->
@@ -39,6 +36,5 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
- <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
</style>
</resources>
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 55e21a76f170..514ea0cc013e 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -517,6 +517,28 @@ public class FileUtilsTest {
}
@Test
+ public void testMalformedTransate_int() throws Exception {
+ try {
+ // The non-standard Linux access mode 3 should throw
+ // an IllegalArgumentException.
+ translateModePosixToPfd(O_RDWR | O_WRONLY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testMalformedTransate_string() throws Exception {
+ try {
+ // The non-standard Linux access mode 3 should throw
+ // an IllegalArgumentException.
+ translateModePosixToString(O_RDWR | O_WRONLY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
public void testTranslateMode_Invalid() throws Exception {
try {
translateModeStringToPosix("rwx");
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6d1aae12d858..f8bd4e33ec75 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -384,6 +384,7 @@ public class SettingsBackupTest {
Settings.Global.PRIV_APP_OOB_LIST,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 96ab977b799c..5a86885459f2 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -278,13 +278,12 @@ public abstract class OverlayBaseTest {
}
}
- final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
- "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
- "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
- "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
- "esse cillum dolore eu fugiat nulla pariatur. " +
- "Excepteur sint occaecat cupidatat non proident, " +
- "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+ + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+ + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+ + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+ + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+ + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
final String so = "Lorem ipsum: single overlay.";
final String mo = "Lorem ipsum: multiple overlays.";
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 6d5276f2423e..27986cce0835 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
+ * 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
+ * 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.
+ * 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.om.hosttest;
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index 8656781e31e5..c7b2dd1ac8f9 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -20,7 +20,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_FLAGS := --no-resource-removal
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
index 06077a79d009..8b5fe99c2e74 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
android:label="Update Overlay Test"/>
</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index a174d774b80e..fef63208b2d3 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
+ * 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
+ * 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.
+ * 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.om.hosttest.update_overlay_test;
@@ -19,9 +19,10 @@ import static org.junit.Assert.assertEquals;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index f95231f1d397..8c00d14ce856 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -9,7 +9,7 @@ LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
frameworks-base-testutils \
mockito-target-minus-junit4
diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml
index 8f490083b16e..ee4a775ac97f 100644
--- a/core/tests/packagemanagertests/AndroidManifest.xml
+++ b/core/tests/packagemanagertests/AndroidManifest.xml
@@ -24,7 +24,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.packagemanager"
android:label="Frameworks PackageManager Core Tests" />
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 4e0f2a8fe060..c5c9700e2316 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -22,10 +22,11 @@ import android.content.Context;
import android.os.FileUtils;
import android.os.ServiceManager;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 3c1526b7dc48..7765977c03e8 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/AndroidManifest.xml b/core/tests/privacytests/AndroidManifest.xml
index a0e52814bdcd..3c826144aa57 100644
--- a/core/tests/privacytests/AndroidManifest.xml
+++ b/core/tests/privacytests/AndroidManifest.xml
@@ -22,7 +22,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.privacy"
android:label="Frameworks Privacy Library Tests" />
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index c88a722c026b..18928ebd6461 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -17,21 +17,20 @@
package android.privacy;
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index 71bd8f1c7bf0..4a353505f1da 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -22,8 +22,9 @@ import static org.junit.Assert.assertTrue;
import android.privacy.internal.rappor.RapporConfig;
import android.privacy.internal.rappor.RapporEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 5c60c8184753..343c07af51df 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -15,7 +15,7 @@ LOCAL_SRC_FILES += src/android/util/IRemoteMemoryIntArray.aidl
LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
frameworks-base-testutils \
mockito-target-minus-junit4 \
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index 8db81ca4b3b6..4ef4b1fe9120 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -51,7 +51,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.utiltests"
android:label="Frameworks Utility Tests" />
diff --git a/core/tests/utiltests/runtests.sh b/core/tests/utiltests/runtests.sh
index 853119f62334..3b46cbbef4e9 100755
--- a/core/tests/utiltests/runtests.sh
+++ b/core/tests/utiltests/runtests.sh
@@ -21,4 +21,4 @@ adb wait-for-device
adb install -r -g "$OUT/data/app/FrameworksUtilTests/FrameworksUtilTests.apk"
-adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/androidx.test.runner.AndroidJUnitRunner'
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index a6120a1e8142..a76c640db74a 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -19,8 +19,9 @@ package android.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/LongArrayTest.java b/core/tests/utiltests/src/android/util/LongArrayTest.java
index a7afcbd81fdf..a9a168b1bf6b 100644
--- a/core/tests/utiltests/src/android/util/LongArrayTest.java
+++ b/core/tests/utiltests/src/android/util/LongArrayTest.java
@@ -19,8 +19,9 @@ package android.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 24b33effdb71..2daefe74eb12 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -24,8 +24,11 @@ import static org.junit.Assert.fail;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
+
import libcore.io.IoUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/RemoteIntArray.java b/core/tests/utiltests/src/android/util/RemoteIntArray.java
index 11d0888179d1..4a3a01e61b7d 100644
--- a/core/tests/utiltests/src/android/util/RemoteIntArray.java
+++ b/core/tests/utiltests/src/android/util/RemoteIntArray.java
@@ -24,7 +24,8 @@ import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
import java.io.Closeable;
import java.io.IOException;
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index b18ee171eb0c..03cf3eb6a2b9 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -29,11 +29,12 @@ import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 000000000000..25dabad6d34c
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C} 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License"};
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Sysconfig files
+
+prebuilt_etc {
+ name: "framework-sysconfig.xml",
+ sub_dir: "sysconfig",
+ src: "framework-sysconfig.xml",
+}
+
+prebuilt_etc {
+ name: "hiddenapi-package-whitelist.xml",
+ sub_dir: "sysconfig",
+ src: "hiddenapi-package-whitelist.xml",
+}
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+ name: "platform.xml",
+ sub_dir: "permissions",
+ src: "platform.xml",
+}
+
+prebuilt_etc {
+ name: "privapp-permissions-platform.xml",
+ sub_dir: "permissions",
+ src: "privapp-permissions-platform.xml",
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.settings",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.settings.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.systemui",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.systemui.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "com.android.timezone.updater.xml",
+ sub_dir: "permissions",
+ src: "com.android.timezone.updater.xml",
+}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
deleted file mode 100644
index 61ef426f510a..000000000000
--- a/data/etc/Android.mk
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework-sysconfig.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp-permissions-platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := hiddenapi-package-whitelist.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.settings
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_MODULE_STEM := com.android.settings.xml
-LOCAL_SRC_FILES := com.android.settings.xml
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.systemui
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_MODULE_STEM := com.android.systemui.xml
-LOCAL_SRC_FILES := com.android.systemui.xml
-include $(BUILD_PREBUILT)
-
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.timezone.updater.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af570b39925b..c21642559bb9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -297,6 +297,8 @@ applications that come with the platform
<permission name="android.permission.POWER_SAVER" />
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
+ <!-- Needed for test only -->
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE" />
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index c84c0354a7c4..f7541e0678d5 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -365,7 +365,7 @@
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
<family lang="und-Cakm">
- <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
</family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 6798ab216734..63a806e53556 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -556,7 +556,7 @@ public class Canvas extends BaseCanvas {
* @hide
*/
public int saveUnclippedLayer(int left, int top, int right, int bottom) {
- return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+ return nSaveUnclippedLayer(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
@@ -1395,6 +1395,8 @@ public class Canvas extends BaseCanvas {
private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
int alpha, int layerFlags);
@CriticalNative
+ private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+ @CriticalNative
private static native boolean nRestore(long canvasHandle);
@CriticalNative
private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 67ad4045868e..515532ffda52 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -189,6 +189,14 @@ public final class RecordingCanvas extends DisplayListCanvas {
nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
}
+ /**
+ * Calls the provided functor that was created via WebViewFunctor_create()
+ * @hide
+ */
+ public void drawWebViewFunctor(int functor) {
+ nDrawWebViewFunctor(mNativeCanvasWrapper, functor);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -303,4 +311,6 @@ public final class RecordingCanvas extends DisplayListCanvas {
@CriticalNative
private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
long propRight, long propBottom, long propRx, long propRy, long propPaint);
+ @CriticalNative
+ private static native void nDrawWebViewFunctor(long canvas, int functor);
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 98af3eb05391..eeaefc5b157c 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,6 +167,7 @@ cc_test {
},
},
data: ["tests/data/**/*.apk"],
+ test_suites: ["device-tests"],
}
cc_benchmark {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c20c720eadbb..5a267804ddf1 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -623,7 +623,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
// Add the pairing of overlayable properties to resource ids to the package
- OverlayableInfo overlayable_info;
+ OverlayableInfo overlayable_info{};
overlayable_info.policy_flags = policy_header->policy_flags;
loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
break;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 6585bfc929ba..7e69e3a8a73f 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,6 +9,8 @@ cc_defaults {
"hwui_lto",
],
+ cpp_std: "experimental",
+
cflags: [
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -226,6 +228,7 @@ cc_defaults {
"RenderProperties.cpp",
"SkiaCanvas.cpp",
"TreeInfo.cpp",
+ "WebViewFunctorManager.cpp",
"VectorDrawable.cpp",
"protos/graphicsstats.proto",
],
@@ -330,6 +333,7 @@ cc_test {
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",
"tests/unit/VectorDrawableAtlasTests.cpp",
+ "tests/unit/WebViewFunctorManagerTests.cpp",
],
}
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 04cf611c8322..bd1e6c5bf2e8 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -18,6 +18,7 @@ X(Flush)
X(Save)
X(Restore)
X(SaveLayer)
+X(SaveBehind)
X(Concat)
X(SetMatrix)
X(Translate)
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index a97c12cad9fd..b9860ada18fc 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -253,7 +253,8 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou
eglDestroySyncKHR(display, fence);
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
+ return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(),
+ Bitmap::computePalette(bitmap));
}
} // namespace android::uirenderer
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0b847af157d1..fc813d20b1ac 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -18,6 +18,7 @@
#include "VectorDrawable.h"
+#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
@@ -116,6 +117,16 @@ struct SaveLayer final : Op {
clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
}
};
+struct SaveBehind final : Op {
+ static const auto kType = Type::SaveBehind;
+ SaveBehind(const SkRect* subset) {
+ if (subset) { this->subset = *subset; }
+ }
+ SkRect subset = kUnset;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ SkAndroidFrameworkUtils::SaveBehind(c, &subset);
+ }
+};
struct Concat final : Op {
static const auto kType = Type::Concat;
@@ -579,6 +590,10 @@ void DisplayListData::saveLayer(const SkRect* bounds, const SkPaint* paint,
this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
}
+void DisplayListData::saveBehind(const SkRect* subset) {
+ this->push<SaveBehind>(0, subset);
+}
+
void DisplayListData::concat(const SkMatrix& matrix) {
this->push<Concat>(0, matrix);
}
@@ -848,6 +863,11 @@ void RecordingCanvas::willRestore() {
fDL->restore();
}
+bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) {
+ fDL->saveBehind(subset);
+ return false;
+}
+
void RecordingCanvas::didConcat(const SkMatrix& matrix) {
fDL->concat(matrix);
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index de8777b4e79a..22b3a63884ee 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -75,6 +75,7 @@ private:
void save();
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
const SkMatrix*, SkCanvas::SaveLayerFlags);
+ void saveBehind(const SkRect*);
void restore();
void concat(const SkMatrix&);
@@ -146,6 +147,7 @@ public:
void willSave() override;
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
void willRestore() override;
+ bool onDoSaveBehind(const SkRect*) override;
void onFlush() override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 00ce28ad196f..1ff7ffe6bf87 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -288,7 +288,10 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
if (mDisplayList) {
- mDisplayList->syncContents();
+ WebViewSyncData syncData {
+ .applyForceDark = info && !info->disableForceDark
+ };
+ mDisplayList->syncContents(syncData);
handleForceDark(info);
}
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6be7ef72cf5d..83b9e7f6a3b8 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
@@ -185,6 +186,11 @@ int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
return this->saveLayer(left, top, right, bottom, nullptr, flags);
}
+int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
+}
+
class SkiaCanvas::Clip {
public:
Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24d9c08ec1c6..4ab0a59447ee 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -74,6 +74,7 @@ public:
SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) override;
+ virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
new file mode 100644
index 000000000000..20e77b453706
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+#include <private/hwui/WebViewFunctor.h>
+#include "Properties.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <atomic>
+
+namespace android::uirenderer {
+
+RenderMode WebViewFunctor_queryPlatformRenderMode() {
+ auto pipelineType = Properties::getRenderPipelineType();
+ switch (pipelineType) {
+ case RenderPipelineType::SkiaGL:
+ return RenderMode::OpenGL_ES;
+ case RenderPipelineType::SkiaVulkan:
+ return RenderMode::Vulkan;
+ default:
+ LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType);
+ }
+}
+
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) {
+ if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
+ ALOGW("Unknown rendermode %d", (int)functorMode);
+ return -1;
+ }
+ if (functorMode == RenderMode::Vulkan &&
+ WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) {
+ ALOGW("Unable to map from GLES platform to a vulkan functor");
+ return -1;
+ }
+ return WebViewFunctorManager::instance().createFunctor(prototype, functorMode);
+}
+
+void WebViewFunctor_release(int functor) {
+ WebViewFunctorManager::instance().releaseFunctor(functor);
+}
+
+static std::atomic_int sNextId{1};
+
+WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) {
+ mFunctor = sNextId++;
+ mCallbacks = callbacks;
+ mMode = functorMode;
+}
+
+WebViewFunctor::~WebViewFunctor() {
+ destroyContext();
+
+ ATRACE_NAME("WebViewFunctor::onDestroy");
+ mCallbacks.onDestroyed(mFunctor);
+}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
+ ATRACE_NAME("WebViewFunctor::sync");
+ mCallbacks.onSync(mFunctor, syncData);
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
+ ATRACE_NAME("WebViewFunctor::drawGl");
+ if (!mHasContext) {
+ mHasContext = true;
+ }
+ mCallbacks.gles.draw(mFunctor, drawInfo);
+}
+
+void WebViewFunctor::destroyContext() {
+ if (mHasContext) {
+ mHasContext = false;
+ ATRACE_NAME("WebViewFunctor::onContextDestroyed");
+ mCallbacks.onContextDestroyed(mFunctor);
+ }
+}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+ static WebViewFunctorManager sInstance;
+ return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode) {
+ auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode);
+ int id = object->id();
+ auto handle = object->createHandle();
+ {
+ std::lock_guard _lock{mLock};
+ mActiveFunctors.push_back(std::move(handle));
+ mFunctors.push_back(std::move(object));
+ }
+ return id;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {
+ sp<WebViewFunctor::Handle> toRelease;
+ {
+ std::lock_guard _lock{mLock};
+ for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) {
+ if ((*iter)->id() == functor) {
+ toRelease = std::move(*iter);
+ mActiveFunctors.erase(iter);
+ break;
+ }
+ }
+ }
+}
+
+void WebViewFunctorManager::onContextDestroyed() {
+ // WARNING: SKETCHY
+ // Because we know that we always remove from mFunctors on RenderThread, the same
+ // thread that always invokes onContextDestroyed, we know that the functor pointers
+ // will remain valid without the lock held.
+ // However, we won't block new functors from being added in the meantime.
+ mLock.lock();
+ const size_t size = mFunctors.size();
+ WebViewFunctor* toDestroyContext[size];
+ for (size_t i = 0; i < size; i++) {
+ toDestroyContext[i] = mFunctors[i].get();
+ }
+ mLock.unlock();
+ for (size_t i = 0; i < size; i++) {
+ toDestroyContext[i]->destroyContext();
+ }
+}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {
+ std::unique_ptr<WebViewFunctor> toRelease;
+ {
+ std::lock_guard _lock{mLock};
+ for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) {
+ if ((*iter)->id() == functor) {
+ toRelease = std::move(*iter);
+ mFunctors.erase(iter);
+ break;
+ }
+ }
+ }
+}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+ std::lock_guard _lock{mLock};
+ for (auto& iter : mActiveFunctors) {
+ if (iter->id() == functor) {
+ return iter;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
new file mode 100644
index 000000000000..2a621dd411e3
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <private/hwui/WebViewFunctor.h>
+#include <renderthread/RenderProxy.h>
+
+#include <utils/LightRefBase.h>
+#include <mutex>
+#include <vector>
+
+namespace android::uirenderer {
+
+class WebViewFunctorManager;
+
+class WebViewFunctor {
+public:
+ WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ ~WebViewFunctor();
+
+ class Handle : public LightRefBase<Handle> {
+ public:
+ ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
+
+ int id() const { return mReference.id(); }
+
+ void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); }
+
+ void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
+
+ private:
+ friend class WebViewFunctor;
+
+ Handle(WebViewFunctor& ref) : mReference(ref) {}
+
+ WebViewFunctor& mReference;
+ };
+
+ int id() const { return mFunctor; }
+ void sync(const WebViewSyncData& syncData) const;
+ void drawGl(const DrawGlInfo& drawInfo);
+ void destroyContext();
+
+ sp<Handle> createHandle() {
+ LOG_ALWAYS_FATAL_IF(mCreatedHandle);
+ mCreatedHandle = true;
+ return sp<Handle>{new Handle(*this)};
+ }
+
+private:
+ WebViewFunctorCallbacks mCallbacks;
+ int mFunctor;
+ RenderMode mMode;
+ bool mHasContext = false;
+ bool mCreatedHandle = false;
+};
+
+class WebViewFunctorManager {
+public:
+ static WebViewFunctorManager& instance();
+
+ int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ void releaseFunctor(int functor);
+ void onContextDestroyed();
+ void destroyFunctor(int functor);
+
+ sp<WebViewFunctor::Handle> handleFor(int functor);
+
+private:
+ WebViewFunctorManager() = default;
+ ~WebViewFunctorManager() = default;
+
+ std::mutex mLock;
+ std::vector<std::unique_ptr<WebViewFunctor>> mFunctors;
+ std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 6c77f9ee1845..6e0258c9ecb2 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -75,20 +75,33 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
}
-static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
- void* addr = calloc(size, 1);
- if (!addr) {
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
+ // Create new ashmem region with read/write priv
+ int fd = ashmem_create_region("bitmap", size);
+ if (fd < 0) {
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
+
+ void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ munmap(addr, size);
+ close(fd);
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
}
-sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
}
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
- return allocateBitmap(bitmap, &android::allocateHeapBitmap);
+ return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
}
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
@@ -97,28 +110,15 @@ sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
return nullptr;
}
- return android::allocateHeapBitmap(size, info, info.minRowBytes());
+ return allocateHeapBitmap(size, info, info.minRowBytes());
}
-sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
- // Create new ashmem region with read/write priv
- int fd = ashmem_create_region("bitmap", size);
- if (fd < 0) {
- return nullptr;
- }
-
- void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED) {
- close(fd);
- return nullptr;
- }
-
- if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
- munmap(addr, size);
- close(fd);
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
+ void* addr = calloc(size, 1);
+ if (!addr) {
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
+ return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
}
void FreePixelRef(void* addr, void* context) {
@@ -132,17 +132,38 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef)
pixelRef.rowBytes()));
}
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
- return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
-}
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace,
+ SkAlphaType alphaType, BitmapPalette palette) {
// As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
- // view the colorspace as RGBA8888.
+ // view the format as RGBA8888.
SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
- kRGBA_8888_SkColorType, kPremul_SkAlphaType,
- colorSpace);
- return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
+ kRGBA_8888_SkColorType, alphaType, colorSpace);
+ return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette));
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+ size_t size, bool readOnly) {
+ if (info.colorType() == kUnknown_SkColorType) {
+ LOG_ALWAYS_FATAL("unknown bitmap configuration");
+ return nullptr;
+ }
+
+ if (!addr) {
+ // Map existing ashmem region if not already mapped.
+ int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
+ size = ashmem_get_size_region(fd);
+ addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ return nullptr;
+ }
+ }
+
+ sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
+ if (readOnly) {
+ bitmap->setImmutable();
+ }
+ return bitmap;
}
void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index d446377ec1d9..2138040d9690 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -54,28 +54,31 @@ typedef void (*FreeFunc)(void* addr, void* context);
class ANDROID_API Bitmap : public SkPixelRef {
public:
+ /* The allocate factories not only construct the Bitmap object but also allocate the
+ * backing store whose type is determined by the specific method that is called.
+ *
+ * The factories that accept SkBitmap* as a param will modify those params by
+ * installing the returned bitmap as their SkPixelRef.
+ *
+ * The factories that accept const SkBitmap& as a param will copy the contents of the
+ * provided bitmap into the newly allocated buffer.
+ */
+ static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
+ static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap);
static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap);
static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
- static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
- static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
- static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
- size_t rowBytes);
-
- static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+ /* The createFrom factories construct a new Bitmap object by wrapping the already allocated
+ * memory that is provided as an input param.
+ */
static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
- sk_sp<SkColorSpace> colorSpace);
-
+ sk_sp<SkColorSpace> colorSpace,
+ SkAlphaType alphaType = kPremul_SkAlphaType,
+ BitmapPalette palette = BitmapPalette::Unknown);
+ static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+ size_t size, bool readOnly);
static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
- Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
- size_t rowBytes);
- Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info,
- BitmapPalette palette = BitmapPalette::Unknown);
-
int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
void reconfigure(const SkImageInfo& info, size_t rowBytes);
@@ -123,6 +126,15 @@ public:
}
private:
+ static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+
+ Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
+ Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
+ size_t rowBytes);
+ Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette);
+
virtual ~Bitmap();
void* getStorage() const;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index a5f21d8e6d73..4c5365d3c170 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -178,6 +178,9 @@ public:
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
virtual void callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) = 0;
+ virtual void drawWebViewFunctor(int /*functor*/) {
+ LOG_ALWAYS_FATAL("Not supported");
+ }
// ----------------------------------------------------------------------------
// Canvas state operations
@@ -193,6 +196,7 @@ public:
SaveFlags::Flags flags) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) = 0;
+ virtual int saveUnclippedLayer(int, int, int, int) = 0;
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index af3a056864a7..cf2f93b95e71 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -21,7 +21,9 @@
#include <SkCanvas.h>
#include <SkDrawable.h>
+#include <WebViewFunctorManager.h>
#include <utils/Functor.h>
+#include <variant>
namespace android {
namespace uirenderer {
@@ -35,17 +37,43 @@ namespace skiapipeline {
class FunctorDrawable : public SkDrawable {
public:
FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ : mBounds(canvas->getLocalClipBounds())
+ , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
+
+ FunctorDrawable(int functor, SkCanvas* canvas)
+ : mBounds(canvas->getLocalClipBounds())
+ , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
+
virtual ~FunctorDrawable() {}
- virtual void syncFunctor() const = 0;
+ virtual void syncFunctor(const WebViewSyncData& data) const {
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->sync(data);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
+ }
+ }
protected:
virtual SkRect onGetBounds() override { return mBounds; }
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
const SkRect mBounds;
+
+ struct LegacyFunctor {
+ explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
+ : functor(functor), listener(listener) {}
+ Functor* functor;
+ sp<GlFunctorLifecycleListener> listener;
+ };
+
+ struct NewFunctor {
+ explicit NewFunctor(int functor) {
+ handle = WebViewFunctorManager::instance().handleFor(functor);
+ }
+ sp<WebViewFunctor::Handle> handle;
+ };
+
+ std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 4a87e7502c6f..240efb41285c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -17,29 +17,28 @@
#include "GLFunctorDrawable.h"
#include <GrContext.h>
#include <private/hwui/DrawGlInfo.h>
+#include "FunctorDrawable.h"
#include "GlFunctorLifecycleListener.h"
+#include "GrBackendSurface.h"
+#include "GrRenderTarget.h"
+#include "GrRenderTargetContext.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
#include "SkRect.h"
-#include "GrBackendSurface.h"
-#include "GrRenderTarget.h"
-#include "GrRenderTargetContext.h"
namespace android {
namespace uirenderer {
namespace skiapipeline {
GLFunctorDrawable::~GLFunctorDrawable() {
- if (mListener.get() != nullptr) {
- mListener->onGlFunctorReleased(mFunctor);
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ lp->listener->onGlFunctorReleased(lp->functor);
+ }
}
}
-void GLFunctorDrawable::syncFunctor() const {
- (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-}
-
static void setScissor(int viewportHeight, const SkIRect& clip) {
SkASSERT(!clip.isEmpty());
// transform to Y-flipped GL space, and prevent negatives
@@ -49,14 +48,14 @@ static void setScissor(int viewportHeight, const SkIRect& clip) {
}
static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
- GrRenderTargetContext *renderTargetContext =
+ GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
if (!renderTargetContext) {
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
return false;
}
- GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget();
+ GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
if (!renderTarget) {
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
return false;
@@ -94,16 +93,16 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
sk_sp<SkSurface> tmpSurface;
// we are in a state where there is an unclipped saveLayer
if (fboID != 0 && !surfaceBounds.contains(clipBounds)) {
-
// create an offscreen layer and clear it
- SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
- tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes,
- surfaceInfo);
+ SkImageInfo surfaceInfo =
+ canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
+ tmpSurface =
+ SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
GrGLFramebufferInfo fboInfo;
if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
- .getGLFramebufferInfo(&fboInfo)) {
+ .getGLFramebufferInfo(&fboInfo)) {
ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
return;
}
@@ -144,7 +143,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
bool clearStencilAfterFunctor = false;
if (CC_UNLIKELY(clipRegion.isComplex())) {
// clear the stencil
- //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
+ // TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
glDisable(GL_SCISSOR_TEST);
glStencilMask(0x1);
glClearStencil(0);
@@ -163,7 +162,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
// GL ops get inserted here if previous flush is missing, which could dirty the stencil
bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
- tmpCanvas->flush(); //need this flush for the single op that draws into the stencil
+ tmpCanvas->flush(); // need this flush for the single op that draws into the stencil
// ensure that the framebuffer that the webview will render into is bound before after we
// draw into the stencil
@@ -188,7 +187,11 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
setScissor(info.height, clipRegion.getBounds());
}
- (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->drawGl(info);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+ }
if (clearStencilAfterFunctor) {
// clear stencil buffer as it may be used by Skia
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 215979cba2e3..2ea4f67428bc 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -31,11 +31,9 @@ namespace skiapipeline {
*/
class GLFunctorDrawable : public FunctorDrawable {
public:
- GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
- virtual ~GLFunctorDrawable();
+ using FunctorDrawable::FunctorDrawable;
- void syncFunctor() const override;
+ virtual ~GLFunctorDrawable();
protected:
void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f08ac17e4082..eed19420a78a 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <utils/MathUtils.h>
#include "LayerDrawable.h"
+#include <utils/MathUtils.h>
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
@@ -44,10 +44,9 @@ static bool shouldFilter(const SkMatrix& matrix) {
if (!matrix.isScaleTranslate()) return true;
// We only care about meaningful scale here
- bool noScale = MathUtils::isOne(matrix.getScaleX())
- && MathUtils::isOne(matrix.getScaleY());
- bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
- && SkScalarIsInt(matrix.getTranslateY());
+ bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+ bool pixelAligned =
+ SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY());
return !(noScale && pixelAligned);
}
@@ -120,11 +119,12 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
// Integer translation is defined as when src rect and dst rect align fractionally.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
- bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
- && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
- == SkScalarFraction(skiaSrcRect.fLeft)
- && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
- == SkScalarFraction(skiaSrcRect.fTop);
+ bool isIntegerTranslate =
+ isBasicallyTranslate(totalMatrix) &&
+ SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) ==
+ SkScalarFraction(skiaSrcRect.fLeft) &&
+ SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) ==
+ SkScalarFraction(skiaSrcRect.fTop);
if (layer->getForceFilter() || !isIntegerTranslate) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 95dc6d0cf096..7cd515ae9fcb 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,8 +32,8 @@ class LayerDrawable : public SkDrawable {
public:
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
- static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
+ static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
+ const SkRect* dstRect, bool useLayerTransform);
protected:
virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4494cb05df10..df1537e2d824 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -127,6 +127,7 @@ public:
mNode.markDrawEnd(mCanvas);
}
}
+
private:
SkCanvas& mCanvas;
RenderNode& mNode;
@@ -140,7 +141,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
// ensures that we paint the layer even if it is not currently visible in the
// event that the properties change and it becomes visible.
if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
- (renderNode->nothingToDraw() && mComposeLayer)) {
+ (renderNode->nothingToDraw() && mComposeLayer)) {
return;
}
@@ -234,8 +235,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
// we need to restrict the portion of the surface drawn to the size of the renderNode.
SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
- canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
- bounds, bounds, &paint);
+ canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
+ bounds, &paint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 073b4814305e..562a3b225e36 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,11 +15,11 @@
*/
#include "ShaderCache.h"
-#include <algorithm>
#include <log/log.h>
-#include <thread>
-#include <array>
#include <openssl/sha.h>
+#include <algorithm>
+#include <array>
+#include <thread>
#include "FileBlobCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
@@ -44,8 +44,7 @@ ShaderCache& ShaderCache::get() {
}
bool ShaderCache::validateCache(const void* identity, ssize_t size) {
- if (nullptr == identity && size == 0)
- return true;
+ if (nullptr == identity && size == 0) return true;
if (nullptr == identity || size < 0) {
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
@@ -66,8 +65,7 @@ bool ShaderCache::validateCache(const void* identity, ssize_t size) {
auto key = sIDKey;
auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
- if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
- return true;
+ if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
ALOGW("ShaderCache::validateCache cache validation fails");
@@ -119,7 +117,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) {
int maxTries = 3;
while (valueSize > mObservedBlobValueSize && maxTries > 0) {
mObservedBlobValueSize = std::min(valueSize, maxValueSize);
- void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+ void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
if (!newValueBuffer) {
free(valueBuffer);
return nullptr;
@@ -133,7 +131,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) {
return nullptr;
}
if (valueSize > mObservedBlobValueSize) {
- ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+ ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
free(valueBuffer);
return nullptr;
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 82804cf93785..d41aadb269dd 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -16,12 +16,12 @@
#pragma once
+#include <GrContextOptions.h>
#include <cutils/compiler.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
-#include <GrContextOptions.h>
namespace android {
@@ -52,7 +52,7 @@ public:
* the initialized state the load and store methods will return without
* performing any cache operations.
*/
- virtual void initShaderDiskCache(const void *identity, ssize_t size);
+ virtual void initShaderDiskCache(const void* identity, ssize_t size);
virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
@@ -153,7 +153,7 @@ private:
/**
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
*/
- size_t mObservedBlobValueSize = 20*1024;
+ size_t mObservedBlobValueSize = 20 * 1024;
/**
* The time in seconds to wait before saving newly inserted cache entries.
@@ -176,7 +176,7 @@ private:
*/
static constexpr uint8_t sIDKey = 0;
- friend class ShaderCacheTestUtils; //used for unit testing
+ friend class ShaderCacheTestUtils; // used for unit testing
};
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index ac6f6a3f776d..230065c222a9 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,9 +27,9 @@ namespace android {
namespace uirenderer {
namespace skiapipeline {
-void SkiaDisplayList::syncContents() {
+void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
for (auto& functor : mChildFunctors) {
- functor->syncFunctor();
+ functor->syncFunctor(data);
}
for (auto& animatedImage : mAnimatedImages) {
animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 45f3a4c33d84..3219ad1deeff 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,11 +16,11 @@
#pragma once
-#include "hwui/AnimatedImageDrawable.h"
#include "FunctorDrawable.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
+#include "hwui/AnimatedImageDrawable.h"
#include "utils/LinearAllocator.h"
#include <deque>
@@ -109,7 +109,7 @@ public:
* NOTE: This function can be folded into RenderNode when we no longer need
* to subclass from DisplayList
*/
- void syncContents();
+ void syncContents(const WebViewSyncData& data);
/**
* ONLY to be called by RenderNode::prepareTree in order to prepare this
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index ea578cb3ec05..e48ecf490c56 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -21,16 +21,16 @@ namespace uirenderer {
namespace skiapipeline {
SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
- : mResourceMap(resourceMap)
- , mItemizeType(itemizeType)
- , mTotalSize("bytes", 0)
- , mPurgeableSize("bytes", 0) {}
+ : mResourceMap(resourceMap)
+ , mItemizeType(itemizeType)
+ , mTotalSize("bytes", 0)
+ , mPurgeableSize("bytes", 0) {}
SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
- : mCategoryKey(categoryKey)
- , mItemizeType(itemizeType)
- , mTotalSize("bytes", 0)
- , mPurgeableSize("bytes", 0) {}
+ : mCategoryKey(categoryKey)
+ , mItemizeType(itemizeType)
+ , mTotalSize("bytes", 0)
+ , mPurgeableSize("bytes", 0) {}
const char* SkiaMemoryTracer::mapName(const char* resourceName) {
for (auto& resource : mResourceMap) {
@@ -42,7 +42,7 @@ const char* SkiaMemoryTracer::mapName(const char* resourceName) {
}
void SkiaMemoryTracer::processElement() {
- if(!mCurrentElement.empty()) {
+ if (!mCurrentElement.empty()) {
// Only count elements that contain "size", other values just provide metadata.
auto sizeResult = mCurrentValues.find("size");
if (sizeResult != mCurrentValues.end()) {
@@ -136,8 +136,8 @@ void SkiaMemoryTracer::logOutput(String8& log) {
for (const auto& typedValue : namedItem.second) {
TraceValue traceValue = convertUnits(typedValue.second);
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
- log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first,
- traceValue.value, traceValue.units, traceValue.count, entry);
+ log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value,
+ traceValue.units, traceValue.count, entry);
}
} else {
auto result = namedItem.second.find("size");
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index abf1f4b052ce..e9a7981ef028 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -50,8 +50,8 @@ public:
}
bool shouldDumpWrappedObjects() const override { return true; }
- void setMemoryBacking(const char*, const char*, const char*) override { }
- void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { }
+ void setMemoryBacking(const char*, const char*, const char*) override {}
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
private:
struct TraceValue {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 7f62ab5abb7d..2e7850d48e54 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -184,15 +184,15 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
} else {
String8 cachesOutput;
mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
- &mRenderThread.renderState());
+ &mRenderThread.renderState());
ALOGE("%s", cachesOutput.string());
if (errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << node->getName();
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
err << ", size " << info.width() << "x" << info.height() << " max size "
- << maxTextureSize << " color type " << (int)info.colorType()
- << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
+ << maxTextureSize << " color type " << (int)info.colorType() << " has context "
+ << (int)(mRenderThread.getGrContext() != nullptr);
errorHandler->onError(err.str());
}
}
@@ -301,8 +301,7 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
mSavePictureProcessor->savePicture(data, mCapturedFile);
} else {
mSavePictureProcessor->savePicture(
- data,
- mCapturedFile + "_" + std::to_string(mCaptureSequence));
+ data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
mCaptureSequence--;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index af58f634ecd0..f2906de4d5a0 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -97,8 +97,7 @@ public:
return mLightCenter;
}
- static void updateLighting(const LightGeometry& lightGeometry,
- const LightInfo& lightInfo) {
+ static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
mLightRadius = lightGeometry.radius;
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b56c3ef94791..6eefed959913 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -24,8 +24,8 @@
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/GLFunctorDrawable.h"
-#include "pipeline/skia/VkInteropFunctorDrawable.h"
#include "pipeline/skia/VkFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -95,8 +95,8 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
drawDrawable(drawable);
}
if (enableReorder) {
- mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
- mDisplayList.get());
+ mCurrentBarrier =
+ mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
drawDrawable(mCurrentBarrier);
}
}
@@ -127,11 +127,25 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
// TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
// interop is disabled/moved.
- functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
- listener, asSkCanvas());
+ functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
+ functor, listener, asSkCanvas());
+ } else {
+ functorDrawable =
+ mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
+ }
+ mDisplayList->mChildFunctors.push_back(functorDrawable);
+ drawDrawable(functorDrawable);
+}
+
+void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
+ FunctorDrawable* functorDrawable;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+ // interop is disabled.
+ functorDrawable =
+ mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
} else {
- functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
- asSkCanvas());
+ functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
}
mDisplayList->mChildFunctors.push_back(functorDrawable);
drawDrawable(functorDrawable);
@@ -167,7 +181,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
if (colorSpaceFilter) {
if (tmpPaint.getColorFilter()) {
tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
- tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+ tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
} else {
tmpPaint.setColorFilter(std::move(colorSpaceFilter));
}
@@ -248,8 +262,7 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch
filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
}
sk_sp<SkImage> image = bitmap.makeImage();
- mRecorder.drawImageLattice(image, lattice, dst,
- filterPaint(std::move(filteredPaint)),
+ mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)),
bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index d6107a9d9969..afeccea3fb70 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -74,6 +74,7 @@ public:
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
virtual void callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) override;
+ void drawWebViewFunctor(int functor) override;
private:
RecordingCanvas mRecorder;
diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h
index 834446905216..fa7f1fe2f746 100644
--- a/libs/hwui/pipeline/skia/SkiaUtils.h
+++ b/libs/hwui/pipeline/skia/SkiaUtils.h
@@ -22,7 +22,7 @@ namespace android {
static inline SkRect SkRectMakeLargest() {
const SkScalar v = SK_ScalarMax;
- return { -v, -v, v, v };
+ return {-v, -v, v, v};
};
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 65ae0ddeccf3..1d3a24463057 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,9 +20,9 @@
#include "Readback.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
+#include "VkInteropFunctorDrawable.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
-#include "VkInteropFunctorDrawable.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -158,7 +158,7 @@ sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThr
ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info()));
+ return Bitmap::createFrom(buffer, skBitmap.refColorSpace());
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 71ad5e17301a..156f74a611a7 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,23 +17,21 @@
#include "VkFunctorDrawable.h"
#include <private/hwui/DrawVkInfo.h>
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
#include <GrBackendDrawableInfo.h>
-#include <thread>
+#include <SkImage.h>
#include <utils/Color.h>
#include <utils/Trace.h>
#include <utils/TraceUtils.h>
-#include <SkImage.h>
#include <vk/GrVkTypes.h>
+#include <thread>
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
namespace skiapipeline {
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
- : INHERITED()
- , mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
VkFunctorDrawHandler::~VkFunctorDrawHandler() {
// TODO(cblume) Fill in the DrawVkInfo parameters.
@@ -55,14 +53,12 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
(*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
}
-VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
-
-VkFunctorDrawable::~VkFunctorDrawable() = default;
-
-void VkFunctorDrawable::syncFunctor() const {
- (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+VkFunctorDrawable::~VkFunctorDrawable() {
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ lp->listener->onGlFunctorReleased(lp->functor);
+ }
+ }
}
void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -71,12 +67,17 @@ void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
}
std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
- GrBackendApi backendApi, const SkMatrix& matrix) {
+ GrBackendApi backendApi, const SkMatrix& matrix) {
if (backendApi != GrBackendApi::kVulkan) {
return nullptr;
}
- std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
- return std::move(draw);
+ std::unique_ptr<VkFunctorDrawHandler> draw;
+ if (mAnyFunctor.index() == 0) {
+ LOG_ALWAYS_FATAL("Not implemented");
+ return nullptr;
+ } else {
+ return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+ }
}
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 5cd131405d15..d6fefc1fca06 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -18,9 +18,9 @@
#include "FunctorDrawable.h"
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
#include <SkImageInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
@@ -36,6 +36,7 @@ public:
~VkFunctorDrawHandler() override;
void draw(const GrBackendDrawableInfo& info) override;
+
private:
typedef GpuDrawHandler INHERITED;
@@ -48,17 +49,15 @@ private:
*/
class VkFunctorDrawable : public FunctorDrawable {
public:
- VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas);
- ~VkFunctorDrawable() override;
+ using FunctorDrawable::FunctorDrawable;
- void syncFunctor() const override;
+ ~VkFunctorDrawable() override;
protected:
// SkDrawable functions:
void onDraw(SkCanvas* canvas) override;
- std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
- const SkMatrix& matrix) override;
+ std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
+ GrBackendApi backendApi, const SkMatrix& matrix) override;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 82285501cb63..a5faae7d5068 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -17,13 +17,13 @@
#include "VkInteropFunctorDrawable.h"
#include <private/hwui/DrawGlInfo.h>
-#include "renderthread/EglManager.h"
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
-#include <thread>
#include <utils/Color.h>
#include <utils/Trace.h>
#include <utils/TraceUtils.h>
+#include <thread>
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
@@ -44,6 +44,7 @@ static renderthread::EglManager sEglManager;
class ScopedDrawRequest {
public:
ScopedDrawRequest() { beginDraw(); }
+
private:
void beginDraw() {
std::lock_guard _lock{sLock};
@@ -57,9 +58,7 @@ private:
}
if (!sEglManager.hasEglContext()) {
- sGLDrawThread->queue().runSync([]() {
- sEglManager.initialize();
- });
+ sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
}
}
};
@@ -93,14 +92,14 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
// Buffer will be used as an OpenGL ES render target.
mFrameBuffer = new GraphicBuffer(
- //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
- static_cast<uint32_t>(surfaceInfo.width()),
- static_cast<uint32_t>(surfaceInfo.height()),
- ColorTypeToPixelFormat(surfaceInfo.colorType()),
- GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
- std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
- "]");
+ // TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+ static_cast<uint32_t>(surfaceInfo.width()),
+ static_cast<uint32_t>(surfaceInfo.height()),
+ ColorTypeToPixelFormat(surfaceInfo.colorType()),
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+ std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+ "]");
status_t error = mFrameBuffer->initCheck();
if (error < 0) {
ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
@@ -110,16 +109,15 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
mFBInfo = surfaceInfo;
}
- //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
- //TODO: draw command has completed.
- //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
- //TODO: GrVkGpu::destroyResources() for example.
+ // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+ // TODO: draw command has completed.
+ // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+ // TODO: GrVkGpu::destroyResources() for example.
bool success = sGLDrawThread->queue().runSync([&]() -> bool {
ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
EGLDisplay display = sEglManager.eglDisplay();
- LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
- "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
- uirenderer::renderthread::EglManager::eglErrorString());
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
// We use an EGLImage to access the content of the GraphicBuffer
// The EGL image is later bound to a 2D texture
EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
@@ -154,10 +152,10 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
AutoGLFramebuffer glFb;
// Bind texture to the frame buffer.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- glTexture.mTexture, 0);
+ glTexture.mTexture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Failed framebuffer check for created target buffer: %s",
- GLUtils::getGLFramebufferError());
+ GLUtils::getGLFramebufferError());
return false;
}
@@ -166,19 +164,22 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
- (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->drawGl(info);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+ }
EGLSyncKHR glDrawFinishedFence =
eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
- "Could not create sync fence %#x", eglGetError());
+ "Could not create sync fence %#x", eglGetError());
glFlush();
// TODO: export EGLSyncKHR in file descr
// TODO: import file desc in Vulkan Semaphore
// TODO: instead block the GPU: probably by using external Vulkan semaphore.
// Block the CPU until the glFlush finish.
- EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
- FENCE_TIMEOUT);
+ EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT);
LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
"Failed to wait for the fence %#x", eglGetError());
eglDestroySyncKHR(display, glDrawFinishedFence);
@@ -197,26 +198,25 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
canvas->resetMatrix();
auto functorImage = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
- nullptr, kBottomLeft_GrSurfaceOrigin);
+ reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr,
+ kBottomLeft_GrSurfaceOrigin);
canvas->drawImage(functorImage, 0, 0, &paint);
canvas->restore();
}
VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
- if (mListener.get() != nullptr) {
- ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- mListener->onGlFunctorReleased(mFunctor);
- });
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync(
+ [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+ }
}
}
-void VkInteropFunctorDrawable::syncFunctor() const {
+void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
- });
+ sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
}
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 8fe52c5ef700..c47ee114263f 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -18,8 +18,8 @@
#include "FunctorDrawable.h"
-#include <utils/RefBase.h>
#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
@@ -32,20 +32,18 @@ namespace skiapipeline {
*/
class VkInteropFunctorDrawable : public FunctorDrawable {
public:
- VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
- virtual ~VkInteropFunctorDrawable();
+ using FunctorDrawable::FunctorDrawable;
- void syncFunctor() const override;
+ virtual ~VkInteropFunctorDrawable();
static void vkInvokeFunctor(Functor* functor);
+ void syncFunctor(const WebViewSyncData& data) const override;
+
protected:
virtual void onDraw(SkCanvas* canvas) override;
private:
-
// Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
sp<GraphicBuffer> mFrameBuffer;
SkImageInfo mFBInfo;
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
new file mode 100644
index 000000000000..e5346aabaee3
--- /dev/null
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+
+#include <private/hwui/DrawGlInfo.h>
+
+namespace android::uirenderer {
+
+enum class RenderMode {
+ OpenGL_ES,
+ Vulkan,
+};
+
+// Static for the lifetime of the process
+RenderMode WebViewFunctor_queryPlatformRenderMode();
+
+struct WebViewSyncData {
+ bool applyForceDark;
+};
+
+struct WebViewFunctorCallbacks {
+ // kModeSync, called on RenderThread
+ void (*onSync)(int functor, const WebViewSyncData& syncData);
+
+ // Called when either the context is destroyed _or_ when the functor's last reference goes
+ // away. Will always be called with an active context and always on renderthread.
+ void (*onContextDestroyed)(int functor);
+
+ // Called when the last reference to the handle goes away and the handle is considered
+ // irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
+ // this functor had ever been drawn.
+ void (*onDestroyed)(int functor);
+
+ union {
+ struct {
+ // Called on RenderThread. initialize is guaranteed to happen before this call
+ void (*draw)(int functor, const DrawGlInfo& params);
+ } gles;
+ // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
+ // what params are valid on what callbacks
+ struct {
+ // Called either the first time the functor is used or the first time it's used after
+ // a call to onContextDestroyed.
+ // void (*initialize)(int functor, const InitParams& params);
+ // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
+ // be useful? Is this useful? */)
+ // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
+ // almost always means something else, and we aren't compositing */);
+ // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
+ // CompositeParams - rename */);
+ } vk;
+ };
+};
+
+// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
+// this function returns. Caller retains full ownership of it.
+// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is released,
+// and it should be considered alive & active until that point.
+void WebViewFunctor_release(int functor);
+
+} // namespace android::uirenderer
+
+#endif // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4e4262c5c0a7..8e57a3a119e3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,8 +576,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) {
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.destroyGlContext();
- thread.vulkanManager().destroy();
+ thread.destroyRenderingContext();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
}
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 085812a00f71..aa6af23d8ed3 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -30,6 +30,7 @@
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include "WebViewFunctorManager.h"
#include <ui/GraphicBuffer.h>
@@ -143,6 +144,14 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
}
}
+void RenderProxy::destroyFunctor(int functor) {
+ ATRACE_CALL();
+ RenderThread& thread = RenderThread::getInstance();
+ thread.queue().post([=]() {
+ WebViewFunctorManager::instance().destroyFunctor(functor);
+ });
+}
+
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
return mRenderThread.queue().runSync([this]() -> auto {
return mContext->createTextureLayer();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d9b789f28f8d..9dc918121be6 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@ public:
ANDROID_API void destroy();
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
+ static void destroyFunctor(int functor);
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API void buildLayer(RenderNode* node);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 207673c1c8dd..c06faddf7fa6 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -132,6 +132,7 @@ RenderThread::RenderThread()
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
+ , mFunctorManager(WebViewFunctorManager::instance())
, mVkManager(nullptr) {
Properties::load();
start("RenderThread");
@@ -197,11 +198,13 @@ void RenderThread::requireGlContext() {
setGrContext(grContext);
}
-void RenderThread::destroyGlContext() {
+void RenderThread::destroyRenderingContext() {
+ mFunctorManager.onContextDestroyed();
if (mEglManager->hasEglContext()) {
setGrContext(nullptr);
mEglManager->destroy();
}
+ vulkanManager().destroy();
}
void RenderThread::dumpGraphicsMemory(int fd) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 2384f9541ec0..12666b323d11 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -23,6 +23,7 @@
#include "CacheManager.h"
#include "TimeLord.h"
#include "thread/ThreadBase.h"
+#include "WebViewFunctorManager.h"
#include <GrContext.h>
#include <SkBitmap.h>
@@ -104,7 +105,7 @@ public:
void dumpGraphicsMemory(int fd);
void requireGlContext();
- void destroyGlContext();
+ void destroyRenderingContext();
/**
* isCurrent provides a way to query, if the caller is running on
@@ -151,6 +152,7 @@ private:
TimeLord mTimeLord;
RenderState* mRenderState;
EglManager* mEglManager;
+ WebViewFunctorManager& mFunctorManager;
ProfileDataContainer mGlobalProfileData;
Readback* mReadback = nullptr;
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 15aec9f291a4..4a2f57e344c4 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -70,7 +70,8 @@ sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
int slot = st.mCurrentTexture;
if (slot != BufferItem::INVALID_BUFFER_SLOT) {
*queueEmpty = true;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
+ st.mCurrentDataSpace);
return mImageSlots[slot].mImage;
}
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index f81202292a49..7aa9b82e3583 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -32,6 +32,8 @@
namespace android {
namespace uirenderer {
+std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
+
SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
int startA = (start >> 24) & 0xff;
int startR = (start >> 16) & 0xff;
@@ -82,12 +84,10 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint
uint32_t length = strlen(text);
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
- canvas->drawText(
- utf16.get(), length, // text buffer
- 0, length, // draw range
- 0, length, // context range
- x, y, minikin::Bidi::LTR,
- glyphPaint, nullptr, nullptr /* measured text */);
+ canvas->drawText(utf16.get(), length, // text buffer
+ 0, length, // draw range
+ 0, length, // context range
+ x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */);
}
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
@@ -96,7 +96,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
- nullptr);
+ nullptr);
}
void TestUtils::TestTask::run() {
@@ -110,11 +110,7 @@ void TestUtils::TestTask::run() {
rtCallback(renderThread);
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- renderThread.vulkanManager().destroy();
- } else {
- renderThread.destroyGlContext();
- }
+ renderThread.destroyRenderingContext();
}
std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index c5db861d4f48..5ff8993e6779 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -27,6 +27,7 @@
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
+#include <gtest/gtest.h>
#include <memory>
namespace android {
@@ -201,8 +202,7 @@ public:
static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
- node.stagingProperties().getWidth(), node.stagingProperties().getHeight(),
- &node));
+ node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
contentCallback(*canvas.get());
node.setStagingDisplayList(canvas->finishRecording());
}
@@ -267,7 +267,14 @@ public:
renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
}
+ static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
+ auto& rt = renderthread::RenderThread::getInstance();
+ rt.queue().runSync([&]() { rtCallback(rt); });
+ }
+
+
static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
+ static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
@@ -296,7 +303,52 @@ public:
static SkRect getClipBounds(const SkCanvas* canvas);
static SkRect getLocalClipBounds(const SkCanvas* canvas);
+ struct CallCounts {
+ int sync = 0;
+ int contextDestroyed = 0;
+ int destroyed = 0;
+ int glesDraw = 0;
+ };
+
+ static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
+
+ static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
+ auto callbacks = WebViewFunctorCallbacks{
+ .onSync =
+ [](int functor, const WebViewSyncData& data) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].sync++;
+ },
+ .onContextDestroyed =
+ [](int functor) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].contextDestroyed++;
+ },
+ .onDestroyed =
+ [](int functor) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].destroyed++;
+ },
+ };
+ switch (mode) {
+ case RenderMode::OpenGL_ES:
+ callbacks.gles.draw = [](int functor, const DrawGlInfo& params) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].glesDraw++;
+ };
+ break;
+ default:
+ ADD_FAILURE();
+ return WebViewFunctorCallbacks{};
+ }
+ return callbacks;
+ }
+
+ static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+
private:
+ static std::unordered_map<int, CallCounts> sMockFunctorCounts;
+
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
MarkAndSweepRemoved observer(nullptr);
node->syncProperties();
@@ -306,9 +358,9 @@ private:
}
auto displayList = node->getDisplayList();
if (displayList) {
- for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
- const_cast<DisplayList*>(displayList))
- ->mChildNodes) {
+ for (auto&& childDr :
+ static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
+ ->mChildNodes) {
syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
}
}
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 448408d19eb1..ec81f629ee45 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -50,7 +50,7 @@ public:
pixels[4000 + 4 * i + 3] = 255;
}
buffer->unlock();
- sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer));
+ sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()));
sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
SkPoint center;
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index a6869791a915..f4c3e13b0ea6 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -19,9 +19,9 @@
#include "tests/common/TestUtils.h"
-#include <gtest/gtest.h>
#include <SkBitmap.h>
#include <SkImage.h>
+#include <gtest/gtest.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 08b967964c59..dac888cd79ca 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@ public:
// current thread can spoof being a GPU thread
static void destroyEglContext() {
if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
+ TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
}
}
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 0331581799b7..c813cd945905 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,9 +355,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
- void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
- mDrawCounter++;
- }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
int getDrawCounter() { return mDrawCounter; }
@@ -370,7 +368,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
[](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
properties.setProjectionReceiver(true);
},
- "B"); // a receiver with an empty display list
+ "B"); // a receiver with an empty display list
auto projectingRipple = TestUtils::createSkiaNode(
0, 0, 100, 100,
@@ -389,14 +387,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
canvas.drawRenderNode(projectingRipple.get());
},
"C");
- auto parent = TestUtils::createSkiaNode(
- 0, 0, 100, 100,
- [&receiverBackground, &child](RenderProperties& properties,
- SkiaRecordingCanvas& canvas) {
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- },
- "A");
+ auto parent =
+ TestUtils::createSkiaNode(0, 0, 100, 100,
+ [&receiverBackground, &child](RenderProperties& properties,
+ SkiaRecordingCanvas& canvas) {
+ canvas.drawRenderNode(receiverBackground.get());
+ canvas.drawRenderNode(child.get());
+ },
+ "A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
@@ -1058,7 +1056,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
public:
FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) override {
+ const SkPaint* paint, SrcRectConstraint constraint) override {
mDrawCounter++;
EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
}
@@ -1076,7 +1074,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
FrameTestCanvas canvas;
RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
canvas.drawDrawable(&drawable);
- EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed
+ EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed
// clean up layer pointer, so we can safely destruct RenderNode
layerNode->setLayerSurface(nullptr);
@@ -1129,15 +1127,14 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
getTotalMatrix());
} else {
// Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
- EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
- matrix);
- EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
- getTotalMatrix());
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
}
}
protected:
int mDrawCounter = 0;
+
private:
bool mFirstDidConcat = true;
};
@@ -1174,14 +1171,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
public:
VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) override {
+ const SkPaint* paint, SrcRectConstraint constraint) override {
const int index = mDrawCounter++;
switch (index) {
case 0:
EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
break;
case 1:
- EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
break;
default:
ADD_FAILURE();
@@ -1191,17 +1188,18 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
VectorDrawable::Group* group = new VectorDrawable::Group();
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
- vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+ vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
- auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
- [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
- vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
- CANVAS_HEIGHT));
- canvas.drawVectorDrawable(vectorDrawable.get());
- vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
- CANVAS_HEIGHT));
- canvas.drawVectorDrawable(vectorDrawable.get());
- });
+ auto node =
+ TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ vectorDrawable->mutateStagingProperties()->setBounds(
+ SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ vectorDrawable->mutateStagingProperties()->setBounds(
+ SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ });
VectorDrawableTestCanvas canvas;
RenderNodeDrawable drawable(node.get(), &canvas, true);
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 75fb0ef0acc1..1cd9bd8ee9d9 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -295,7 +295,8 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
canvasContext->destroy();
}
-RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+// TODO: Is this supposed to work in SkiaGL/SkiaVK?
+RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
VectorDrawable::Group* group = new VectorDrawable::Group();
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 1433aa0349f4..87981f115763 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-#include <dirent.h>
#include <cutils/properties.h>
-#include <cstdint>
+#include <dirent.h>
#include <errno.h>
+#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <utils/Log.h>
-#include "pipeline/skia/ShaderCache.h"
+#include <cstdint>
#include "FileBlobCache.h"
+#include "pipeline/skia/ShaderCache.h"
using namespace android::uirenderer::skiapipeline;
@@ -66,7 +66,6 @@ public:
} /* namespace uirenderer */
} /* namespace android */
-
namespace {
std::string getExternalStorageFolder() {
@@ -82,14 +81,12 @@ bool folderExist(const std::string& folderName) {
return false;
}
-inline bool
-checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
- return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
- && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
+inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+ return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
+ 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
}
-inline bool
-checkShader(const sk_sp<SkData>& shader, const char* program) {
+inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
return checkShader(shader, shader2);
}
@@ -116,32 +113,31 @@ void genRandomData(std::vector<T>& buffer) {
}
}
-
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
TEST(ShaderCacheTest, testWriteAndRead) {
if (!folderExist(getExternalStorageFolder())) {
- //don't run the test if external storage folder is not available
+ // don't run the test if external storage folder is not available
return;
}
- std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
- std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
- //remove any test files from previous test run
+ // remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
std::srand(0);
- //read the cache from a file that does not exist
+ // read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
ShaderCache::get().initShaderDiskCache();
- //read a key - should not be found since the cache is empty
+ // read a key - should not be found since the cache is empty
sk_sp<SkData> outVS;
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
- //write to the in-memory cache without storing on disk and verify we read the same values
+ // write to the in-memory cache without storing on disk and verify we read the same values
sk_sp<SkData> inVS;
setShader(inVS, "sassas");
ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
@@ -152,23 +148,23 @@ TEST(ShaderCacheTest, testWriteAndRead) {
ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS, "someVS"));
- //store content to disk and release in-memory cache
+ // store content to disk and release in-memory cache
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
- //change to a file that does not exist and verify load fails
+ // change to a file that does not exist and verify load fails
ShaderCache::get().setFilename(cacheFile2.c_str());
ShaderCache::get().initShaderDiskCache();
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
- //load again content from disk from an existing file and check the data is read correctly
+ // load again content from disk from an existing file and check the data is read correctly
ShaderCache::get().setFilename(cacheFile1.c_str());
ShaderCache::get().initShaderDiskCache();
sk_sp<SkData> outVS2;
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS2, "someVS"));
- //change data, store to disk, read back again and verify data has been changed
+ // change data, store to disk, read back again and verify data has been changed
setShader(inVS, "ewData1");
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -176,9 +172,8 @@ TEST(ShaderCacheTest, testWriteAndRead) {
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS2, "ewData1"));
-
- //write and read big data chunk (50K)
- size_t dataSize = 50*1024;
+ // write and read big data chunk (50K)
+ size_t dataSize = 50 * 1024;
std::vector<uint8_t> dataBuffer(dataSize);
genRandomData(dataBuffer);
setShader(inVS, dataBuffer);
@@ -194,31 +189,31 @@ TEST(ShaderCacheTest, testWriteAndRead) {
TEST(ShaderCacheTest, testCacheValidation) {
if (!folderExist(getExternalStorageFolder())) {
- //don't run the test if external storage folder is not available
+ // don't run the test if external storage folder is not available
return;
}
- std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
- std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
- //remove any test files from previous test run
+ // remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
std::srand(0);
- //generate identity and read the cache from a file that does not exist
+ // generate identity and read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
std::vector<uint8_t> identity(1024);
genRandomData(identity);
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
// generate random content in cache and store to disk
constexpr size_t numBlob(10);
constexpr size_t keySize(1024);
constexpr size_t dataSize(50 * 1024);
- std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+ std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
for (auto& blob : blobVec) {
std::vector<uint8_t> keyBuffer(keySize);
std::vector<uint8_t> dataBuffer(dataSize);
@@ -237,47 +232,47 @@ TEST(ShaderCacheTest, testCacheValidation) {
// change to a file that does not exist and verify validation fails
ShaderCache::get().setFilename(cacheFile2.c_str());
ShaderCache::get().initShaderDiskCache();
- ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
// restore the original file and verify validation succeeds
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
- ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
for (const auto& blob : blobVec) {
auto outVS = ShaderCache::get().load(*blob.first.get());
- ASSERT_TRUE( checkShader(outVS, blob.second) );
+ ASSERT_TRUE(checkShader(outVS, blob.second));
}
// generate error identity and verify load fails
ShaderCache::get().initShaderDiskCache(identity.data(), -1);
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
- ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ nullptr, identity.size() * sizeof(decltype(identity)::value_type));
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
// verify the cache validation again after load fails
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
- ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
for (const auto& blob : blobVec) {
auto outVS = ShaderCache::get().load(*blob.first.get());
- ASSERT_TRUE( checkShader(outVS, blob.second) );
+ ASSERT_TRUE(checkShader(outVS, blob.second));
}
// generate another identity and verify load fails
for (auto& data : identity) {
data += std::rand();
}
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 415f9e8517ff..53bf84f13fd6 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -100,16 +100,35 @@ TEST(SkiaDisplayList, syncContexts) {
GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
skiaDL.mChildFunctors.push_back(&functorDrawable);
+ int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ auto& counts = TestUtils::countsForFunctor(functor2);
+ skiaDL.mChildFunctors.push_back(
+ skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
+ WebViewFunctor_release(functor2);
+
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
vectorDrawable.mutateStagingProperties()->setBounds(bounds);
skiaDL.mVectorDrawables.push_back(&vectorDrawable);
// ensure that the functor and vectorDrawable are properly synced
- skiaDL.syncContents();
-
- ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
- ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+ TestUtils::runOnRenderThread([&](auto&) {
+ skiaDL.syncContents(WebViewSyncData{
+ .applyForceDark = false,
+ });
+ });
+
+ EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
+ EXPECT_EQ(counts.sync, 1);
+ EXPECT_EQ(counts.destroyed, 0);
+ EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+
+ skiaDL.reset();
+ TestUtils::runOnRenderThread([](auto&) {
+ // Fence
+ });
+ EXPECT_EQ(counts.destroyed, 1);
}
class ContextFactory : public IContextFactory {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index d16b8be89e20..3c06dab36fe4 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -24,10 +24,10 @@
#include "DamageAccumulator.h"
#include "IContextFactory.h"
#include "SkiaCanvas.h"
-#include "pipeline/skia/SkiaUtils.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "pipeline/skia/SkiaUtils.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -51,8 +51,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
auto surface = SkSurface::MakeRasterN32Premul(1, 1);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -84,8 +83,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -106,12 +104,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
@@ -130,8 +126,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
@@ -203,38 +198,32 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// Single draw, should be white.
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
// 1 Overdraw, should be blue blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
// 2 Overdraw, should be green blended onto white
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
// 3 Overdraw, should be pink blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
// 4 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
// 5 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
}
@@ -389,7 +378,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
EXPECT_FALSE(pipeline->isSurfaceReady());
EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
EXPECT_TRUE(pipeline->isSurfaceReady());
- renderThread.destroyGlContext();
+ renderThread.destroyRenderingContext();
EXPECT_FALSE(pipeline->isSurfaceReady());
}
-
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index b645aeb55074..1a09b1c52d8a 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -54,9 +54,9 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
- std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
- std::move(typeface), data, st.st_size, fileName, 0,
- std::vector<minikin::FontVariation>());
+ std::shared_ptr<minikin::MinikinFont> font =
+ std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
+ std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index b11eaa9d6a2f..5db002862fcd 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,8 +85,10 @@ const static TestData sTestDataSet[] = {
outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
- outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
- outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
+ outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0,
+ 10.0);
+ outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0,
+ 20.0);
}},
// Check box VectorDrawable path data
@@ -157,7 +159,8 @@ const static TestData sTestDataSet[] = {
},
[](SkPath* outPath) {
outPath->moveTo(300.0, 70.0);
- outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
+ outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction,
+ 301.0, 70.0);
outPath->close();
outPath->moveTo(300.0, 70.0);
}},
@@ -236,14 +239,14 @@ struct StringPath {
};
const StringPath sStringPaths[] = {
- {"3e...3", false}, // Not starting with a verb and ill-formatted float
- {"L.M.F.A.O", false}, // No floats following verbs
- {"m 1 1", true}, // Valid path data
- {"\n \t z", true}, // Valid path data with leading spaces
- {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
- {"f 4 5", false}, // Invalid verb
- {"\r ", false}, // Empty string
- {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
+ {"3e...3", false}, // Not starting with a verb and ill-formatted float
+ {"L.M.F.A.O", false}, // No floats following verbs
+ {"m 1 1", true}, // Valid path data
+ {"\n \t z", true}, // Valid path data with leading spaces
+ {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+ {"f 4 5", false}, // Invalid verb
+ {"\r ", false}, // Empty string
+ {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
};
static bool hasSameVerbs(const PathData& from, const PathData& to) {
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
new file mode 100644
index 000000000000..c8169aff1c5e
--- /dev/null
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "WebViewFunctorManager.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/TestUtils.h"
+
+#include <unordered_map>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(WebViewFunctor, createDestroyGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ WebViewFunctor_release(functor);
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // Empty, don't care
+ });
+ auto& counts = TestUtils::countsForFunctor(functor);
+ // We never initialized, so contextDestroyed == 0
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncHandleGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor));
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ auto& counts = TestUtils::countsForFunctor(functor);
+ EXPECT_EQ(0, counts.sync);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ });
+
+ EXPECT_EQ(1, counts.sync);
+
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ });
+
+ EXPECT_EQ(2, counts.sync);
+
+ handle.clear();
+
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncDrawGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ auto& counts = TestUtils::countsForFunctor(functor);
+ for (int i = 0; i < 5; i++) {
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ handle->drawGl(drawInfo);
+ });
+ }
+ handle.clear();
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ EXPECT_EQ(5, counts.sync);
+ EXPECT_EQ(10, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, contextDestroyed) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ auto& counts = TestUtils::countsForFunctor(functor);
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ });
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(1, counts.glesDraw);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
+ rt.destroyRenderingContext();
+ });
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(1, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(2, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ handle.clear();
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(2, counts.glesDraw);
+ EXPECT_EQ(2, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+} \ No newline at end of file
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index aecceb3609f5..63d15403b607 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,14 +17,14 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "Properties.h"
#include "debug/GlesDriver.h"
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
-#include "Properties.h"
#include "tests/common/LeakChecker.h"
-#include "thread/TaskProcessor.h"
#include "thread/Task.h"
#include "thread/TaskManager.h"
+#include "thread/TaskProcessor.h"
#include <signal.h>
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4daccda78e23..4473ce632b1b 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
#define COLOR_H
#include <math.h>
+#include <cutils/compiler.h>
#include <system/graphics.h>
#include <ui/PixelFormat.h>
@@ -117,7 +118,7 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
-sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
struct Lab {
float L;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 80d8e72a87e2..0a90f85cda0e 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,6 +89,10 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.animationPending = false;
+ mLocked.displayWidth = -1;
+ mLocked.displayHeight = -1;
+ mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
+
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -106,6 +110,15 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
+
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+
+ loadResources();
+
+ if (mLocked.pointerIcon.isValid()) {
+ mLocked.pointerIconChanged = true;
+ updatePointerLocked();
+ }
}
PointerController::~PointerController() {
@@ -131,15 +144,23 @@ bool PointerController::getBounds(float* outMinX, float* outMinY,
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
-
- if (!mLocked.viewport.isValid()) {
+ if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
return false;
}
- *outMinX = mLocked.viewport.logicalLeft;
- *outMinY = mLocked.viewport.logicalTop;
- *outMaxX = mLocked.viewport.logicalRight - 1;
- *outMaxY = mLocked.viewport.logicalBottom - 1;
+ *outMinX = 0;
+ *outMinY = 0;
+ switch (mLocked.displayOrientation) {
+ case DISPLAY_ORIENTATION_90:
+ case DISPLAY_ORIENTATION_270:
+ *outMaxX = mLocked.displayHeight - 1;
+ *outMaxY = mLocked.displayWidth - 1;
+ break;
+ default:
+ *outMaxX = mLocked.displayWidth - 1;
+ *outMaxY = mLocked.displayHeight - 1;
+ break;
+ }
return true;
}
@@ -210,12 +231,6 @@ void PointerController::getPosition(float* outX, float* outY) const {
*outY = mLocked.pointerY;
}
-int32_t PointerController::getDisplayId() const {
- AutoMutex _l(mLock);
-
- return mLocked.viewport.displayId;
-}
-
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -340,57 +355,48 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResourcesLocked();
- updatePointerLocked();
-}
+ loadResources();
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
- if (viewport.orientation == DISPLAY_ORIENTATION_90
- || viewport.orientation == DISPLAY_ORIENTATION_270) {
- width = viewport.deviceHeight;
- height = viewport.deviceWidth;
- } else {
- width = viewport.deviceWidth;
- height = viewport.deviceHeight;
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
}
+
+ mLocked.presentationChanged = true;
+ updatePointerLocked();
}
-void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
AutoMutex _l(mLock);
- if (viewport == mLocked.viewport) {
- return;
- }
- const DisplayViewport oldViewport = mLocked.viewport;
- mLocked.viewport = viewport;
-
- int32_t oldDisplayWidth, oldDisplayHeight;
- getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
- int32_t newDisplayWidth, newDisplayHeight;
- getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+ // Adjust to use the display's unrotated coordinate frame.
+ if (orientation == DISPLAY_ORIENTATION_90
+ || orientation == DISPLAY_ORIENTATION_270) {
+ int32_t temp = height;
+ height = width;
+ width = temp;
+ }
- // Reset cursor position to center if size or display changed.
- if (oldViewport.displayId != viewport.displayId
- || oldDisplayWidth != newDisplayWidth
- || oldDisplayHeight != newDisplayHeight) {
+ if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
+ mLocked.displayWidth = width;
+ mLocked.displayHeight = height;
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
- // Reload icon resources for density may be changed.
- loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- } else if (oldViewport.orientation != viewport.orientation) {
+ }
+
+ if (mLocked.displayOrientation != orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -399,37 +405,37 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
float temp;
// Undo the previous rotation.
- switch (oldViewport.orientation) {
+ switch (mLocked.displayOrientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = oldViewport.deviceHeight - y;
+ x = mLocked.displayWidth - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = oldViewport.deviceWidth - x;
- y = oldViewport.deviceHeight - y;
+ x = mLocked.displayWidth - x;
+ y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = oldViewport.deviceWidth - temp;
+ y = mLocked.displayHeight - temp;
break;
}
// Perform the new rotation.
- switch (viewport.orientation) {
+ switch (orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = viewport.deviceHeight - temp;
+ y = mLocked.displayWidth - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = viewport.deviceWidth - x;
- y = viewport.deviceHeight - y;
+ x = mLocked.displayWidth - x;
+ y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = viewport.deviceWidth - y;
+ x = mLocked.displayHeight - y;
y = temp;
break;
}
@@ -438,6 +444,7 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
+ mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -607,16 +614,11 @@ void PointerController::removeInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
- if (!mLocked.viewport.isValid()) {
- return;
- }
-
+void PointerController::updatePointerLocked() {
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
- mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -727,18 +729,8 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() {
}
}
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+void PointerController::loadResources() {
mPolicy->loadPointerResources(&mResources);
-
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
- }
-
- mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index a32cc42a3342..7f4e5a59c9b6 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,7 +23,6 @@
#include <vector>
#include <ui/DisplayInfo.h>
-#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -97,7 +96,6 @@ public:
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
- virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -108,7 +106,7 @@ public:
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(const DisplayViewport& viewport);
+ void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -158,7 +156,9 @@ private:
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- DisplayViewport viewport;
+ int32_t displayWidth;
+ int32_t displayHeight;
+ int32_t displayOrientation;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@ private:
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked GUARDED_BY(mLock);
+ } mLocked;
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@ private:
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResourcesLocked();
+ void loadResources();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3a94d6..eb2bc98ec9e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,16 +144,13 @@ void SpriteController::doUpdateSprites() {
}
}
- // Resize and/or reparent sprites if needed.
+ // Resize sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
- if (update.state.surfaceControl == nullptr) {
- continue;
- }
- if (update.state.wantSurfaceVisible()) {
+ if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -173,12 +170,6 @@ void SpriteController::doUpdateSprites() {
}
}
}
-
- // If surface is a new one, we have to set right layer stack.
- if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
- t.setLayerStack(update.state.surfaceControl, update.state.displayId);
- needApplyTransaction = true;
- }
}
if (needApplyTransaction) {
t.apply();
@@ -245,7 +236,7 @@ void SpriteController::doUpdateSprites() {
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -454,15 +445,6 @@ void SpriteController::SpriteImpl::setTransformationMatrix(
}
}
-void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
- AutoMutex _l(mController->mLock);
-
- if (mLocked.state.displayId != displayId) {
- mLocked.state.displayId = displayId;
- invalidateLocked(DIRTY_DISPLAY_ID);
- }
-}
-
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f50d113..31e43e9b99e5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,9 +125,6 @@ public:
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
-
- /* Sets the id of the display where the sprite should be shown. */
- virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -173,7 +170,6 @@ private:
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
- DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -184,7 +180,7 @@ private:
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
+ positionX(0), positionY(0), layer(0), alpha(1.0f),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -197,7 +193,6 @@ private:
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
- int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -230,7 +225,6 @@ private:
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
- virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index b2fd8ecef734..3dcf69426362 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -12,7 +12,7 @@ LOCAL_PACKAGE_NAME := FrameworksLocationTests
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
core-test-rules \
guava \
mockito-target-minus-junit4 \
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index ddb8ea6aa53a..5010d3d56a50 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -29,7 +29,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.locationtests"
android:label="Frameworks Location Tests" />
</manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
index bb6547bec0f7..7bddb58f2cf2 100644
--- a/location/tests/locationtests/AndroidTest.xml
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="FrameworksLocationTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.locationtests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 9ada216d5e95..de76aeff82c4 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,7 +18,9 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.media.audiofx.AudioEffect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -27,6 +29,8 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -48,7 +52,7 @@ import java.util.Objects;
public final class AudioRecordingConfiguration implements Parcelable {
private final static String TAG = new String("AudioRecordingConfiguration");
- private final int mSessionId;
+ private final int mClientSessionId;
private final int mClientSource;
@@ -60,18 +64,50 @@ public final class AudioRecordingConfiguration implements Parcelable {
private final int mPatchHandle;
+ private final int mClientPortId;
+
+ private boolean mClientSilenced;
+
+ private final int mDeviceSource;
+
+ private final AudioEffect.Descriptor[] mClientEffects;
+
+ private final AudioEffect.Descriptor[] mDeviceEffects;
+
/**
* @hide
*/
+ @TestApi
public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
- AudioFormat devFormat, int patchHandle, String packageName) {
+ AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
+ boolean clientSilenced, int deviceSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
mClientUid = uid;
- mSessionId = session;
+ mClientSessionId = session;
mClientSource = source;
mClientFormat = clientFormat;
mDeviceFormat = devFormat;
mPatchHandle = patchHandle;
mClientPackageName = packageName;
+ mClientPortId = clientPortId;
+ mClientSilenced = clientSilenced;
+ mDeviceSource = deviceSource;
+ mClientEffects = clientEffects;
+ mDeviceEffects = deviceEffects;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public AudioRecordingConfiguration(int uid, int session, int source,
+ AudioFormat clientFormat, AudioFormat devFormat,
+ int patchHandle, String packageName) {
+ this(uid, session, source, clientFormat,
+ devFormat, patchHandle, packageName, 0 /*clientPortId*/,
+ false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
+ new AudioEffect.Descriptor[0] /*clientEffects*/,
+ new AudioEffect.Descriptor[0] /*deviceEffects*/);
}
/**
@@ -87,13 +123,26 @@ public final class AudioRecordingConfiguration implements Parcelable {
* @hide
*/
public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
- return new String("session:" + arc.mSessionId
- + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ String clientEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mClientEffects) {
+ clientEffects += "'" + desc.name + "' ";
+ }
+ String deviceEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
+ deviceEffects += "'" + desc.name + "' ";
+ }
+
+ return new String("session:" + arc.mClientSessionId
+ + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ " -- uid:" + arc.mClientUid
+ " -- patch:" + arc.mPatchHandle
+ " -- pack:" + arc.mClientPackageName
+ " -- format client=" + arc.mClientFormat.toLogFriendlyString()
- + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ + " -- silenced:" + arc.mClientSilenced
+ + " -- effects client=" + clientEffects
+ + ", dev=" + deviceEffects);
}
// Note that this method is called server side, so no "privileged" information is ever sent
@@ -106,8 +155,10 @@ public final class AudioRecordingConfiguration implements Parcelable {
*/
public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
- in.mSessionId, in.mClientSource, in.mClientFormat,
- in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
+ in.mClientSessionId, in.mClientSource, in.mClientFormat,
+ in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
+ in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+ in.mDeviceEffects);
}
// matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
@@ -129,16 +180,8 @@ public final class AudioRecordingConfiguration implements Parcelable {
// documented return values match the sources that return false
// in MediaRecorder.isSystemOnlyAudioSource(source)
/**
- * Returns the audio source being used for the recording.
- * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
- * {@link MediaRecorder.AudioSource#MIC},
- * {@link MediaRecorder.AudioSource#VOICE_UPLINK},
- * {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
- * {@link MediaRecorder.AudioSource#VOICE_CALL},
- * {@link MediaRecorder.AudioSource#CAMCORDER},
- * {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
- * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
- * {@link MediaRecorder.AudioSource#UNPROCESSED}.
+ * Returns the audio source selected by the client.
+ * @return the audio source selected by the client.
*/
public @AudioSource int getClientAudioSource() { return mClientSource; }
@@ -146,7 +189,9 @@ public final class AudioRecordingConfiguration implements Parcelable {
* Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
* @return the session number.
*/
- public int getClientAudioSessionId() { return mSessionId; }
+ public int getClientAudioSessionId() {
+ return mClientSessionId;
+ }
/**
* Returns the audio format at which audio is recorded on this Android device.
@@ -223,6 +268,54 @@ public final class AudioRecordingConfiguration implements Parcelable {
return null;
}
+ /**
+ * Returns the system unique ID assigned for the AudioRecord object corresponding to this
+ * AudioRecordingConfiguration client.
+ * @return the port ID.
+ */
+ int getClientPortId() {
+ return mClientPortId;
+ }
+
+ /**
+ * Returns true if the audio returned to the client is currently being silenced by the
+ * audio framework due to concurrent capture policy (e.g the capturing application does not have
+ * an active foreground process or service anymore).
+ * @return true if captured audio is silenced, false otherwise .
+ */
+ public boolean isClientSilenced() {
+ return mClientSilenced;
+ }
+
+ /**
+ * Returns the audio source currently used to configure the capture path. It can be different
+ * from the source returned by {@link #getClientAudioSource()} if another capture is active.
+ * @return the audio source active on the capture path.
+ */
+ public @AudioSource int getAudioSource() {
+ return mDeviceSource;
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the capture stream.
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
+ * capture stream. This can be different from the list returned by {@link #getClientEffects()}
+ * if another capture is active.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
+ }
+
public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
= new Parcelable.Creator<AudioRecordingConfiguration>() {
/**
@@ -240,7 +333,7 @@ public final class AudioRecordingConfiguration implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mSessionId, mClientSource);
+ return Objects.hash(mClientSessionId, mClientSource);
}
@Override
@@ -250,23 +343,45 @@ public final class AudioRecordingConfiguration implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mSessionId);
+ dest.writeInt(mClientSessionId);
dest.writeInt(mClientSource);
mClientFormat.writeToParcel(dest, 0);
mDeviceFormat.writeToParcel(dest, 0);
dest.writeInt(mPatchHandle);
dest.writeString(mClientPackageName);
dest.writeInt(mClientUid);
+ dest.writeInt(mClientPortId);
+ dest.writeBoolean(mClientSilenced);
+ dest.writeInt(mDeviceSource);
+ dest.writeInt(mClientEffects.length);
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i].writeToParcel(dest, 0);
+ }
+ dest.writeInt(mDeviceEffects.length);
+ for (int i = 0; i < mDeviceEffects.length; i++) {
+ mDeviceEffects[i].writeToParcel(dest, 0);
+ }
}
private AudioRecordingConfiguration(Parcel in) {
- mSessionId = in.readInt();
+ mClientSessionId = in.readInt();
mClientSource = in.readInt();
mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
mPatchHandle = in.readInt();
mClientPackageName = in.readString();
mClientUid = in.readInt();
+ mClientPortId = in.readInt();
+ mClientSilenced = in.readBoolean();
+ mDeviceSource = in.readInt();
+ mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
+ mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
}
@Override
@@ -277,11 +392,16 @@ public final class AudioRecordingConfiguration implements Parcelable {
AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
return ((mClientUid == that.mClientUid)
- && (mSessionId == that.mSessionId)
+ && (mClientSessionId == that.mClientSessionId)
&& (mClientSource == that.mClientSource)
&& (mPatchHandle == that.mPatchHandle)
&& (mClientFormat.equals(that.mClientFormat))
&& (mDeviceFormat.equals(that.mDeviceFormat))
- && (mClientPackageName.equals(that.mClientPackageName)));
+ && (mClientPackageName.equals(that.mClientPackageName))
+ && (mClientPortId == that.mClientPortId)
+ && (mClientSilenced == that.mClientSilenced)
+ && (mDeviceSource == that.mDeviceSource)
+ && (mClientEffects.equals(that.mClientEffects))
+ && (mDeviceEffects.equals(that.mDeviceEffects)));
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 36f635a8e572..58fc1ab1c4dd 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.os.Build;
import android.util.Log;
@@ -334,7 +335,9 @@ public class AudioSystem
* @param packName package name of the client app performing the recording. NOT SUPPORTED
*/
void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingFormat, String packName);
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
+ int activeSource, String packName);
}
private static AudioRecordingCallback sRecordingCallback;
@@ -352,19 +355,27 @@ public class AudioSystem
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
+ * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
+ boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the description of the record format.
*/
@UnsupportedAppUsage
private static void recordingCallbackFromNative(int event, int uid, int session, int source,
- int[] recordingFormat) {
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
+ int activeSource) {
AudioRecordingCallback cb = null;
synchronized (AudioSystem.class) {
cb = sRecordingCallback;
}
+
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
if (cb != null) {
// TODO receive package name from native
- cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
+ cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+ recordingFormat, clientEffects, effects, activeSource, "");
}
}
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbed1a13..e29bd006c00a 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@ public class FileDataSourceDesc extends DataSourceDesc {
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
+ private int mCount = 0;
+ private boolean mClosed = false;
private FileDataSourceDesc() {
super();
@@ -55,27 +57,52 @@ public class FileDataSourceDesc extends DataSourceDesc {
@Override
void close() {
super.close();
- closeFD();
+ decCount();
}
/**
- * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ * Decrements usage count by {@link MediaPlayer2}.
+ * If this is the last usage, also releases the file descriptor held by this
+ * {@code FileDataSourceDesc} object.
*/
- void closeFD() {
+ void decCount() {
synchronized (this) {
- if (mPFD != null) {
- try {
- mPFD.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close pfd: " + e);
- }
-
- mPFD = null;
+ --mCount;
+ if (mCount > 0) {
+ return;
+ }
+
+ try {
+ mPFD.close();
+ mClosed = true;
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+ }
+ }
+
+ /**
+ * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+ */
+ void incCount() {
+ synchronized (this) {
+ if (!mClosed) {
+ ++mCount;
}
}
}
/**
+ * Return the status of underline ParcelFileDescriptor
+ * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+ */
+ boolean isPFDClosed() {
+ synchronized (this) {
+ return mClosed;
+ }
+ }
+
+ /**
* Return the ParcelFileDescriptor of this data source.
* @return the ParcelFileDescriptor of this data source
*/
@@ -150,6 +177,16 @@ public class FileDataSourceDesc extends DataSourceDesc {
* @return a new {@link FileDataSourceDesc} object
*/
public @NonNull FileDataSourceDesc build() {
+ if (mPFD == null) {
+ throw new IllegalStateException(
+ "underline ParcelFileDescriptor should not be null");
+ }
+ try {
+ mPFD.getFd();
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException("ParcelFileDescriptor has been closed");
+ }
+
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
dsd.mPFD = mPFD;
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
new file mode 100644
index 000000000000..aa2a937956c5
--- /dev/null
+++ b/media/java/android/media/MediaItem2.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class with information on a single media item with the metadata information. Here are use
+ * cases.
+ * <ul>
+ * <li>Specify media items to {@link SessionPlayer2} for playback.
+ * <li>Share media items across the processes.
+ * </ul>
+ * <p>
+ * Subclasses of the session player may only accept certain subclasses of the media items. Check
+ * the player documentation that you're interested in.
+ * <p>
+ * When it's shared across the processes, we cannot guarantee that they contain the right values
+ * because media items are application dependent especially for the metadata.
+ * <p>
+ * This object is thread safe.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link androidx.media2.MediaItem} for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public class MediaItem2 implements Parcelable {
+ private static final String TAG = "MediaItem2";
+
+ // intentionally less than long.MAX_VALUE.
+ // Declare this first to avoid 'illegal forward reference'.
+ static final long LONG_MAX = 0x7ffffffffffffffL;
+
+ /**
+ * Used when a position is unknown.
+ *
+ * @see #getEndPosition()
+ */
+ public static final long POSITION_UNKNOWN = LONG_MAX;
+
+ public static final Parcelable.Creator<MediaItem2> CREATOR =
+ new Parcelable.Creator<MediaItem2>() {
+ @Override
+ public MediaItem2 createFromParcel(Parcel in) {
+ return new MediaItem2(in);
+ }
+
+ @Override
+ public MediaItem2[] newArray(int size) {
+ return new MediaItem2[size];
+ }
+ };
+
+ // TODO: Use SessionPlayer2.UNKNOWN_TIME instead
+ private static final long UNKNOWN_TIME = -1;
+
+ private final long mStartPositionMs;
+ private final long mEndPositionMs;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private MediaMetadata mMetadata;
+ @GuardedBy("mLock")
+ private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>();
+
+ /**
+ * Used by {@link MediaItem2.Builder}.
+ */
+ // Note: Needs to be protected when we want to allow 3rd party player to define customized
+ // MediaItem2.
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(Builder builder) {
+ this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
+ }
+
+ /**
+ * Used by Parcelable.Creator.
+ */
+ // Note: Needs to be protected when we want to allow 3rd party player to define customized
+ // MediaItem2.
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(Parcel in) {
+ this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong());
+ }
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(MediaItem2 item) {
+ this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs);
+ }
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) {
+ if (startPositionMs > endPositionMs) {
+ throw new IllegalArgumentException("Illegal start/end position: "
+ + startPositionMs + " : " + endPositionMs);
+ }
+ if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+ long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+ if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN
+ && endPositionMs > durationMs) {
+ throw new IllegalArgumentException("endPositionMs shouldn't be greater than"
+ + " duration in the metdata, endPositionMs=" + endPositionMs
+ + ", durationMs=" + durationMs);
+ }
+ }
+ mMetadata = metadata;
+ mStartPositionMs = startPositionMs;
+ mEndPositionMs = endPositionMs;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+ synchronized (mLock) {
+ sb.append("{mMetadata=").append(mMetadata);
+ sb.append(", mStartPositionMs=").append(mStartPositionMs);
+ sb.append(", mEndPositionMs=").append(mEndPositionMs);
+ sb.append('}');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Sets metadata. If the metadata is not {@code null}, its id should be matched with this
+ * instance's media id.
+ *
+ * @param metadata metadata to update
+ * @see MediaMetadata#METADATA_KEY_MEDIA_ID
+ */
+ public void setMetadata(@Nullable MediaMetadata metadata) {
+ List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>();
+ synchronized (mLock) {
+ if (mMetadata != null && metadata != null
+ && !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) {
+ Log.d(TAG, "MediaItem2's media ID shouldn't be changed");
+ return;
+ }
+ mMetadata = metadata;
+ listeners.addAll(mListeners);
+ }
+
+ for (Pair<OnMetadataChangedListener, Executor> pair : listeners) {
+ final OnMetadataChangedListener listener = pair.first;
+ pair.second.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onMetadataChanged(MediaItem2.this);
+ }
+ });
+ }
+ }
+
+ /**
+ * Gets the metadata of the media.
+ *
+ * @return metadata from the session
+ */
+ public @Nullable MediaMetadata getMetadata() {
+ synchronized (mLock) {
+ return mMetadata;
+ }
+ }
+
+ /**
+ * Return the position in milliseconds at which the playback will start.
+ * @return the position in milliseconds at which the playback will start
+ */
+ public long getStartPosition() {
+ return mStartPositionMs;
+ }
+
+ /**
+ * Return the position in milliseconds at which the playback will end.
+ * {@link #POSITION_UNKNOWN} means ending at the end of source content.
+ * @return the position in milliseconds at which the playback will end
+ */
+ public long getEndPosition() {
+ return mEndPositionMs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mMetadata, 0);
+ dest.writeLong(mStartPositionMs);
+ dest.writeLong(mEndPositionMs);
+ }
+
+ /**
+ * Gets the media id for this item. If it's not {@code null}, it's a persistent unique key
+ * for the underlying media content.
+ *
+ * @return media Id from the session
+ */
+ @Nullable String getMediaId() {
+ synchronized (mLock) {
+ return mMetadata != null
+ ? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null;
+ }
+ }
+
+ void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) {
+ synchronized (mLock) {
+ for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) {
+ if (pair.first == listener) {
+ return;
+ }
+ }
+ mListeners.add(new Pair<>(listener, executor));
+ }
+ }
+
+ void removeOnMetadataChangedListener(OnMetadataChangedListener listener) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ if (mListeners.get(i).first == listener) {
+ mListeners.remove(i);
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Builder for {@link MediaItem2}.
+ */
+ public static class Builder {
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaMetadata mMetadata;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ long mStartPositionMs = 0;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ long mEndPositionMs = POSITION_UNKNOWN;
+
+ /**
+ * Set the metadata of this instance. {@code null} for unset.
+ *
+ * @param metadata metadata
+ * @return this instance for chaining
+ */
+ public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) {
+ mMetadata = metadata;
+ return this;
+ }
+
+ /**
+ * Sets the start position in milliseconds at which the playback will start.
+ * Any negative number is treated as 0.
+ *
+ * @param position the start position in milliseconds at which the playback will start
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setStartPosition(long position) {
+ if (position < 0) {
+ position = 0;
+ }
+ mStartPositionMs = position;
+ return this;
+ }
+
+ /**
+ * Sets the end position in milliseconds at which the playback will end.
+ * Any negative number is treated as maximum length of the media item.
+ *
+ * @param position the end position in milliseconds at which the playback will end
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setEndPosition(long position) {
+ if (position < 0) {
+ position = POSITION_UNKNOWN;
+ }
+ mEndPositionMs = position;
+ return this;
+ }
+
+ /**
+ * Build {@link MediaItem2}.
+ *
+ * @return a new {@link MediaItem2}.
+ */
+ public @NonNull MediaItem2 build() {
+ return new MediaItem2(this);
+ }
+ }
+
+ interface OnMetadataChangedListener {
+ void onMetadataChanged(MediaItem2 item);
+ }
+}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 00a393a902b4..d656fa359826 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -40,8 +40,7 @@ import java.util.Map;
* MediaMetadataRetriever class provides a unified interface for retrieving
* frame and meta data from an input media file.
*/
-public class MediaMetadataRetriever
-{
+public class MediaMetadataRetriever implements AutoCloseable {
static {
System.loadLibrary("media_jni");
native_init();
@@ -672,6 +671,11 @@ public class MediaMetadataRetriever
@UnsupportedAppUsage
private native byte[] getEmbeddedPicture(int pictureType);
+ @Override
+ public void close() {
+ release();
+ }
+
/**
* Call it when one is done with the object. This method releases the memory
* allocated internally.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 18d36eb1f753..0057875ec3f4 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.content.ContentProvider;
@@ -1680,37 +1679,6 @@ public class MediaPlayer extends PlayerBase
public native boolean isPlaying();
/**
- * Gets the current buffering management params used by the source component.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the current buffering management params used by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- * @hide
- */
- @NonNull
- @TestApi
- public native BufferingParams getBufferingParams();
-
- /**
- * Sets buffering management params.
- * The object sets its internal BufferingParams to the input, except that the input is
- * invalid or not supported.
- * Call it only after {@code setDataSource} has been called.
- * The input is a hint to MediaPlayer.
- *
- * @param params the buffering management params.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released, or {@code setDataSource} has not been called.
- * @throws IllegalArgumentException if params is invalid or not supported.
- * @hide
- */
- @TestApi
- public native void setBufferingParams(@NonNull BufferingParams params);
-
- /**
* Change playback speed of audio by resampling the audio.
* <p>
* Specifies resampling as audio mode for variable rate playback, i.e.,
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d4b1c7f868cb..a3702d68cc49 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -544,32 +544,55 @@ public class MediaPlayer2 implements AutoCloseable
public native long getCurrentPosition();
/**
- * Gets the duration of the file.
+ * Gets the duration of the dsd.
*
+ * @param dsd the descriptor of data source of which you want to get duration
* @return the duration in milliseconds, if no duration is available
* (for example, if streaming live content), -1 is returned.
+ * @throws NullPointerException if dsd is null
*/
- public native long getDuration();
+ public long getDuration(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return -1;
+ }
+
+ return native_getDuration(sourceInfo.mId);
+ }
+
+ private native long native_getDuration(long srcId);
/**
- * Gets the current buffered media source position received through progressive downloading.
+ * Gets the buffered media source position of given dsd.
* For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
* has already been played indicates that the next 3000 milliseconds of the
* content to play has been buffered.
*
+ * @param dsd the descriptor of data source of which you want to get buffered position
* @return the current buffered media source position in milliseconds
+ * @throws NullPointerException if dsd is null
*/
- public long getBufferedPosition() {
+ public long getBufferedPosition(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return 0;
+ }
+
// Use cached buffered percent for now.
- int bufferedPercentage;
- synchronized (mSrcLock) {
- if (mCurrentSourceInfo == null) {
- bufferedPercentage = 0;
- } else {
- bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
- }
+ int bufferedPercentage = sourceInfo.mBufferedPercentage.get();
+
+ long duration = getDuration(dsd);
+ if (duration < 0) {
+ duration = 0;
}
- return getDuration() * bufferedPercentage / 100;
+
+ return duration * bufferedPercentage / 100;
}
/**
@@ -673,7 +696,7 @@ public class MediaPlayer2 implements AutoCloseable
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
int state = getState();
try {
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -706,7 +729,7 @@ public class MediaPlayer2 implements AutoCloseable
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
synchronized (mSrcLock) {
clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
@@ -732,22 +755,56 @@ public class MediaPlayer2 implements AutoCloseable
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
+ boolean hasError = false;
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ hasError = true;
+ continue;
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ hasError = true;
+ continue;
+ }
- synchronized (mSrcLock) {
- clearNextSourceInfos_l();
+ fdsd.incCount();
+ }
+ }
+ if (hasError) {
for (DataSourceDesc dsd : dsds) {
if (dsd != null) {
- mNextSourceInfos.add(new SourceInfo(dsd));
- } else {
- Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
+ dsd.close();
}
}
+ throw new IllegalArgumentException("invalid data source list");
+ }
+
+ synchronized (mSrcLock) {
+ clearNextSourceInfos_l();
+ for (DataSourceDesc dsd : dsds) {
+ mNextSourceInfos.add(new SourceInfo(dsd));
+ }
}
prepareNextDataSource();
}
});
}
+ // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+ private void checkDataSourceDesc(DataSourceDesc dsd) {
+ if (dsd != null) {
+ throw new IllegalArgumentException("dsd is expected to be non null");
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+ }
+ fdsd.incCount();
+ }
+ }
+
/**
* Removes all data sources pending to be played.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -1467,7 +1524,6 @@ public class MediaPlayer2 implements AutoCloseable
private native PersistableBundle native_getMetrics();
-
/**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
@@ -1505,7 +1561,6 @@ public class MediaPlayer2 implements AutoCloseable
private native void native_setBufferingParams(@NonNull BufferingParams params);
-
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
* PlaybackParams to the input. This allows the object to resume at previous speed
@@ -1969,19 +2024,31 @@ public class MediaPlayer2 implements AutoCloseable
/**
* Returns a List of track information.
*
+ * @param dsd the descriptor of data source of which you want to get track info
* @return List of track info. The total number of tracks is the array length.
* Must be called again if an external timed text source has been added after
* addTimedTextSource method is called.
* @throws IllegalStateException if it is called in an invalid state.
+ * @throws NullPointerException if dsd is null
*/
- public @NonNull List<TrackInfo> getTrackInfo() {
- TrackInfo[] trackInfo = getInbandTrackInfo();
+
+ public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return new ArrayList<TrackInfo>(0);
+ }
+
+ TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo);
return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
}
- private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
+ private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException {
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.build();
PlayerMessage response = invoke(request);
if (response == null) {
@@ -2001,9 +2068,10 @@ public class MediaPlayer2 implements AutoCloseable
/**
* Returns the index of the audio, video, or subtitle track currently selected for playback,
- * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
- * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+ * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+ * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
*
+ * @param dsd the descriptor of data source of which you want to get selected track
* @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
* {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
* {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
@@ -2011,14 +2079,24 @@ public class MediaPlayer2 implements AutoCloseable
* a negative integer is returned when there is no selected track for {@code trackType} or
* when {@code trackType} is not one of audio, video, or subtitle.
* @throws IllegalStateException if called after {@link #close()}
+ * @throws NullPointerException if dsd is null
*
- * @see #getTrackInfo()
- * @see #selectTrack(int)
- * @see #deselectTrack(int)
+ * @see #getTrackInfo
+ * @see #selectTrack
+ * @see #deselectTrack
*/
- public int getSelectedTrack(int trackType) {
+ public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return -1;
+ }
+
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.addValues(Value.newBuilder().setInt32Value(trackType))
.build();
PlayerMessage response = invoke(request);
@@ -2049,19 +2127,20 @@ public class MediaPlayer2 implements AutoCloseable
* In addition, the support for selecting an audio track at runtime is pretty limited
* in that an audio track can only be selected in the <em>Prepared</em> state.
* </p>
+ * @param dsd the descriptor of data source of which you want to select track
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * each individual track can be found by calling {@link #getTrackInfo} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
// This is an asynchronous call.
- public Object selectTrack(int index) {
+ public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
@Override
void process() {
- selectOrDeselectTrack(index, true /* select */);
+ selectOrDeselectTrack(dsd, index, true /* select */);
}
});
}
@@ -2073,28 +2152,37 @@ public class MediaPlayer2 implements AutoCloseable
* deselected. If the timed text track identified by index has not been
* selected before, it throws an exception.
* </p>
+ * @param dsd the descriptor of data source of which you want to deselect track
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * each individual track can be found by calling {@link #getTrackInfo} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
// This is an asynchronous call.
- public Object deselectTrack(int index) {
+ public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
@Override
void process() {
- selectOrDeselectTrack(index, false /* select */);
+ selectOrDeselectTrack(dsd, index, false /* select */);
}
});
}
- private void selectOrDeselectTrack(int index, boolean select)
- throws IllegalStateException {
+ private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return;
+ }
+
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(
select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.addValues(Value.newBuilder().setInt32Value(index))
.build();
invoke(request);
@@ -2568,7 +2656,7 @@ public class MediaPlayer2 implements AutoCloseable
* Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
* {@link TimedMetaData}.
*
- * @see MediaPlayer2#selectTrack(int)
+ * @see MediaPlayer2#selectTrack
* @see MediaPlayer2.OnTimedMetaDataAvailableListener
* @see TimedMetaData
*
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
deleted file mode 100644
index a4265525fb6b..000000000000
--- a/media/java/android/media/MediaPlayerBase.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Base class for all media players that want media session.
- */
-public abstract class MediaPlayerBase implements AutoCloseable {
- /**
- * @hide
- */
- @IntDef({
- PLAYER_STATE_IDLE,
- PLAYER_STATE_PAUSED,
- PLAYER_STATE_PLAYING,
- PLAYER_STATE_ERROR })
- @Retention(RetentionPolicy.SOURCE)
- public @interface PlayerState {}
-
- /**
- * @hide
- */
- @IntDef({
- BUFFERING_STATE_UNKNOWN,
- BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
- BUFFERING_STATE_BUFFERING_AND_STARVED,
- BUFFERING_STATE_BUFFERING_COMPLETE })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BuffState {}
-
- /**
- * State when the player is idle, and needs configuration to start playback.
- */
- public static final int PLAYER_STATE_IDLE = 0;
-
- /**
- * State when the player's playback is paused
- */
- public static final int PLAYER_STATE_PAUSED = 1;
-
- /**
- * State when the player's playback is ongoing
- */
- public static final int PLAYER_STATE_PLAYING = 2;
-
- /**
- * State when the player is in error state and cannot be recovered self.
- */
- public static final int PLAYER_STATE_ERROR = 3;
-
- /**
- * Buffering state is unknown.
- */
- public static final int BUFFERING_STATE_UNKNOWN = 0;
-
- /**
- * Buffering state indicating the player is buffering but enough has been buffered
- * for this player to be able to play the content.
- * See {@link #getBufferedPosition()} for how far is buffered already.
- */
- public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
-
- /**
- * Buffering state indicating the player is buffering, but the player is currently starved
- * for data, and cannot play.
- */
- public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
-
- /**
- * Buffering state indicating the player is done buffering, and the remainder of the content is
- * available for playback.
- */
- public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
-
- /**
- * Starts or resumes playback.
- */
- public abstract void play();
-
- /**
- * Prepares the player for playback.
- * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
- * notified when the preparation phase completed. During this time, the player may allocate
- * resources required to play, such as audio and video decoders.
- */
- public abstract void prepare();
-
- /**
- * Pauses playback.
- */
- public abstract void pause();
-
- /**
- * Resets the MediaPlayerBase to its uninitialized state.
- */
- public abstract void reset();
-
- /**
- *
- */
- public abstract void skipToNext();
-
- /**
- * Moves the playback head to the specified position
- * @param pos the new playback position expressed in ms.
- */
- public abstract void seekTo(long pos);
-
- public static final long UNKNOWN_TIME = -1;
-
- /**
- * Gets the current playback head position.
- * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
- */
- public long getCurrentPosition() { return UNKNOWN_TIME; }
-
- /**
- * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
- * @return the duration in ms, or {@link #UNKNOWN_TIME}.
- */
- public long getDuration() { return UNKNOWN_TIME; }
-
- /**
- * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
- * @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
- */
- public long getBufferedPosition() { return UNKNOWN_TIME; }
-
- /**
- * Returns the current player state.
- * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
- * notification of changes.
- * @return the current player state
- */
- public abstract @PlayerState int getPlayerState();
-
- /**
- * Returns the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- * @return the buffering state.
- */
- public abstract @BuffState int getBufferingState();
-
- /**
- * Sets the {@link AudioAttributes} to be used during the playback of the media.
- *
- * @param attributes non-null <code>AudioAttributes</code>.
- */
- public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
-
- /**
- * Returns AudioAttributes that media player has.
- */
- public abstract @Nullable AudioAttributes getAudioAttributes();
-
- /**
- * Sets the data source to be played.
- * @param dsd
- */
- public abstract void setDataSource(@NonNull DataSourceDesc dsd);
-
- /**
- * Sets the data source that will be played immediately after the current one is done playing.
- * @param dsd
- */
- public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
-
- /**
- * Sets the list of data sources that will be sequentially played after the current one. Each
- * data source is played immediately after the previous one is done playing.
- * @param dsds
- */
- public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
-
- /**
- * Returns the current data source.
- * @return the current data source, or null if none is set, or none available to play.
- */
- public abstract @Nullable DataSourceDesc getCurrentDataSource();
-
- /**
- * Configures the player to loop on the current data source.
- * @param loop true if the current data source is meant to loop.
- */
- public abstract void loopCurrent(boolean loop);
-
- /**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed
- */
- public abstract void setPlaybackSpeed(float speed);
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- public float getPlaybackSpeed() { return 1.0f; }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- public boolean isReversePlaybackSupported() { return false; }
-
- /**
- * Sets the volume of the audio of the media to play, expressed as a linear multiplier
- * on the audio samples.
- * Note that this volume is specific to the player, and is separate from stream volume
- * used across the platform.<br>
- * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
- * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
- * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- */
- public abstract void setPlayerVolume(float volume);
-
- /**
- * Returns the current volume of this player to this player.
- * Note that it does not take into account the associated stream volume.
- * @return the player volume.
- */
- public abstract float getPlayerVolume();
-
- /**
- * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
- */
- public float getMaxPlayerVolume() { return 1.0f; }
-
- /**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- public abstract void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb);
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
- /**
- * A callback class to receive notifications for events on the media player.
- * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
- * register this callback.
- */
- public static abstract class PlayerEventCallback {
- /**
- * Called when the player's current data source has changed.
- *
- * @param mpb the player whose data source changed.
- * @param dsd the new current data source. null, if no more data sources available.
- */
- public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
- @Nullable DataSourceDesc dsd) { }
- /**
- * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
- * referenced by the given data source.
- * @param mpb the player that is prepared.
- * @param dsd the data source that the player is prepared to play.
- */
- public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
-
- /**
- * Called to indicate that the state of the player has changed.
- * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
- * @param mpb the player whose state has changed.
- * @param state the new state of the player.
- */
- public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
-
- /**
- * Called to report buffering events for a data source.
- * @param mpb the player that is buffering
- * @param dsd the data source for which buffering is happening.
- * @param state the new buffering state.
- */
- public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
- @NonNull DataSourceDesc dsd, @BuffState int state) { }
-
- /**
- * Called to indicate that the playback speed has changed.
- * @param mpb the player that has changed the playback speed.
- * @param speed the new playback speed.
- */
- public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
-
- /**
- * Called to indicate that {@link #seekTo(long)} is completed.
- *
- * @param mpb the player that has completed seeking.
- * @param position the previous seeking request.
- * @see #seekTo(long)
- */
- public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
- }
-
-}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fd1406078e7a..f07076ad14aa 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -16,33 +16,53 @@
package android.media;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
+import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC;
+import static android.os.Environment.MEDIA_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore.Images;
+import android.provider.MediaStore.ThumbnailConstants;
import android.util.Log;
+import android.util.Size;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.ToIntFunction;
/**
- * Thumbnail generation routines for media provider.
+ * Utilities for generating visual thumbnails from files.
*/
-
public class ThumbnailUtils {
private static final String TAG = "ThumbnailUtils";
- /* Maximum pixels size for created bitmap. */
- private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
- private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120;
- private static final int UNCONSTRAINED = -1;
+ /** @hide */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
/* Options used internally. */
private static final int OPTIONS_NONE = 0x0;
@@ -54,153 +74,252 @@ public class ThumbnailUtils {
*/
public static final int OPTIONS_RECYCLE_INPUT = 0x2;
+ private static Size convertKind(int kind) {
+ if (kind == ThumbnailConstants.MICRO_KIND) {
+ return Point.convert(ThumbnailConstants.MICRO_SIZE);
+ } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+ return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE);
+ } else if (kind == ThumbnailConstants.MINI_KIND) {
+ return Point.convert(ThumbnailConstants.MINI_SIZE);
+ } else {
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+ }
+ }
+
+ private static class Resizer implements ImageDecoder.OnHeaderDecodedListener {
+ private final Size size;
+ private final CancellationSignal signal;
+
+ public Resizer(Size size, CancellationSignal signal) {
+ this.size = size;
+ this.signal = signal;
+ }
+
+ @Override
+ public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
+
+ // We don't know how clients will use the decoded data, so we have
+ // to default to the more flexible "software" option.
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.max(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
+ }
+ }
+
/**
- * Constant used to indicate the dimension of mini thumbnail.
- * @hide Only used by media framework and media provider internally.
+ * Create a thumbnail for given audio file.
+ *
+ * @param filePath The audio file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
*/
- public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
+ @Deprecated
+ public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) {
+ try {
+ return createAudioThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
/**
- * Constant used to indicate the dimension of micro thumbnail.
- * @hide Only used by media framework and media provider internally.
+ * Create a thumbnail for given audio file.
+ *
+ * @param file The audio file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
*/
- @UnsupportedAppUsage
- public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
+ public static @NonNull Bitmap createAudioThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ final Resizer resizer = new Resizer(size, signal);
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(file.getAbsolutePath());
+ final byte[] raw = retriever.getEmbeddedPicture();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ }
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
+ }
+
+ // Only poke around for files on external storage
+ if (MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(file))) {
+ throw new IOException("No embedded album art found");
+ }
+
+ // Ignore "Downloads" or top-level directories
+ final File parent = file.getParentFile();
+ final File grandParent = parent != null ? parent.getParentFile() : null;
+ if (parent != null
+ && parent.getName().equals(Environment.DIRECTORY_DOWNLOADS)) {
+ throw new IOException("No thumbnails in Downloads directories");
+ }
+ if (grandParent != null
+ && MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(grandParent))) {
+ throw new IOException("No thumbnails in top-level directories");
+ }
+
+ // If no embedded image found, look around for best standalone file
+ final File[] found = ArrayUtils
+ .defeatNullable(file.getParentFile().listFiles((dir, name) -> {
+ final String lower = name.toLowerCase();
+ return (lower.endsWith(".jpg") || lower.endsWith(".png"));
+ }));
+
+ final ToIntFunction<File> score = (f) -> {
+ final String lower = f.getName().toLowerCase();
+ if (lower.equals("albumart.jpg")) return 4;
+ if (lower.startsWith("albumart") && lower.endsWith(".jpg")) return 3;
+ if (lower.contains("albumart") && lower.endsWith(".jpg")) return 2;
+ if (lower.endsWith(".jpg")) return 1;
+ return 0;
+ };
+ final Comparator<File> bestScore = (a, b) -> {
+ return score.applyAsInt(a) - score.applyAsInt(b);
+ };
+
+ final File bestFile = Arrays.asList(found).stream().max(bestScore).orElse(null);
+ if (bestFile == null) {
+ throw new IOException("No album art found");
+ }
+
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(bestFile), resizer);
+ }
/**
- * This method first examines if the thumbnail embedded in EXIF is bigger than our target
- * size. If not, then it'll create a thumbnail from original image. Due to efficiency
- * consideration, we want to let MediaThumbRequest avoid calling this method twice for
- * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
- *
- * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
+ * Create a thumbnail for given image file.
*
- * @param filePath the path of image file
- * @param kind could be MINI_KIND or MICRO_KIND
- * @return Bitmap, or null on failures
+ * @param filePath The image file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+ */
+ @Deprecated
+ public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) {
+ try {
+ return createImageThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
+
+ /**
+ * Create a thumbnail for given image file.
*
- * @hide This method is only used by media framework and media provider internally.
+ * @param file The audio file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
*/
- @UnsupportedAppUsage
- public static Bitmap createImageThumbnail(String filePath, int kind) {
- boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
- int targetSize = wantMini
- ? TARGET_SIZE_MINI_THUMBNAIL
- : TARGET_SIZE_MICRO_THUMBNAIL;
- int maxPixels = wantMini
- ? MAX_NUM_PIXELS_THUMBNAIL
- : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
- SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
- Bitmap bitmap = null;
- String mimeType = MediaFile.getMimeTypeForFile(filePath);
+ public static @NonNull Bitmap createImageThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ final Resizer resizer = new Resizer(size, signal);
+ final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
if (mimeType.equals("image/heif")
|| mimeType.equals("image/heif-sequence")
|| mimeType.equals("image/heic")
|| mimeType.equals("image/heic-sequence")) {
- bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels);
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(file.getAbsolutePath());
+ return retriever.getThumbnailImageAtIndex(-1,
+ new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
+ size.getWidth() * size.getHeight());
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
+ }
} else if (MediaFile.isExifMimeType(mimeType)) {
- createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
- bitmap = sizedThumbnailBitmap.mBitmap;
+ final ExifInterface exif = new ExifInterface(file);
+ final byte[] raw = exif.getThumbnailBytes();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ }
}
- if (bitmap == null) {
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(filePath);
- FileDescriptor fd = stream.getFD();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = 1;
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(fd, null, options);
- if (options.mCancel || options.outWidth == -1
- || options.outHeight == -1) {
- return null;
- }
- options.inSampleSize = computeSampleSize(
- options, targetSize, maxPixels);
- options.inJustDecodeBounds = false;
-
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
- } catch (IOException ex) {
- Log.e(TAG, "", ex);
- } catch (OutOfMemoryError oom) {
- Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
- } finally {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException ex) {
- Log.e(TAG, "", ex);
- }
- }
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
- }
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
+ }
- if (kind == Images.Thumbnails.MICRO_KIND) {
- // now we make it a "square thumbnail" for MICRO_KIND thumbnail
- bitmap = extractThumbnail(bitmap,
- TARGET_SIZE_MICRO_THUMBNAIL,
- TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
+ /**
+ * Create a thumbnail for given video file.
+ *
+ * @param filePath The video file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+ */
+ @Deprecated
+ public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) {
+ try {
+ return createVideoThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
}
- return bitmap;
}
/**
- * Create a video thumbnail for a video. May return null if the video is
- * corrupt or the format is not supported.
+ * Create a thumbnail for given video file.
*
- * @param filePath the path of video file
- * @param kind could be MINI_KIND or MICRO_KIND
+ * @param file The video file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
*/
- public static Bitmap createVideoThumbnail(String filePath, int kind) {
- Bitmap bitmap = null;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- retriever.setDataSource(filePath);
- // First retrieve album art in metadata if set.
- byte[] embeddedPicture = retriever.getEmbeddedPicture();
- if (embeddedPicture != null && embeddedPicture.length > 0) {
- bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
+ public static @NonNull Bitmap createVideoThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ final Resizer resizer = new Resizer(size, signal);
+ try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+ mmr.setDataSource(file.getAbsolutePath());
+
+ // Try to retrieve thumbnail from metadata
+ final byte[] raw = mmr.getEmbeddedPicture();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
}
- // Fall back to first frame of the video.
- if (bitmap == null) {
- bitmap = retriever.getFrameAtTime(-1);
- }
- } catch (IllegalArgumentException ex) {
- // Assume this is a corrupt video file
- } catch (RuntimeException ex) {
- // Assume this is a corrupt video file.
- } finally {
- try {
- retriever.release();
- } catch (RuntimeException ex) {
- // Ignore failures while cleaning up.
- }
- }
- if (bitmap == null) return null;
-
- if (kind == Images.Thumbnails.MINI_KIND) {
- // Scale down the bitmap if it's too large.
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- int max = Math.max(width, height);
- if (max > 512) {
- float scale = 512f / max;
- int w = Math.round(scale * width);
- int h = Math.round(scale * height);
- bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+ // Fall back to middle of video
+ final int width = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH));
+ final int height = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT));
+ final long duration = Long.parseLong(mmr.extractMetadata(METADATA_KEY_DURATION));
+
+ // If we're okay with something larger than native format, just
+ // return a frame without up-scaling it
+ if (size.getWidth() > width && size.getHeight() > height) {
+ return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+ } else {
+ return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+ size.getWidth(), size.getHeight());
}
- } else if (kind == Images.Thumbnails.MICRO_KIND) {
- bitmap = extractThumbnail(bitmap,
- TARGET_SIZE_MICRO_THUMBNAIL,
- TARGET_SIZE_MICRO_THUMBNAIL,
- OPTIONS_RECYCLE_INPUT);
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
}
- return bitmap;
}
/**
@@ -242,122 +361,27 @@ public class ThumbnailUtils {
return thumbnail;
}
- /*
- * Compute the sample size as a function of minSideLength
- * and maxNumOfPixels.
- * minSideLength is used to specify that minimal width or height of a
- * bitmap.
- * maxNumOfPixels is used to specify the maximal size in pixels that is
- * tolerable in terms of memory usage.
- *
- * The function returns a sample size based on the constraints.
- * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
- * which indicates no care of the corresponding constraint.
- * The functions prefers returning a sample size that
- * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
- *
- * Also, the function rounds up the sample size to a power of 2 or multiple
- * of 8 because BitmapFactory only honors sample size this way.
- * For example, BitmapFactory downsamples an image by 2 even though the
- * request is 3. So we round up the sample size to avoid OOM.
- */
+ @Deprecated
@UnsupportedAppUsage
private static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
- int initialSize = computeInitialSampleSize(options, minSideLength,
- maxNumOfPixels);
-
- int roundedSize;
- if (initialSize <= 8 ) {
- roundedSize = 1;
- while (roundedSize < initialSize) {
- roundedSize <<= 1;
- }
- } else {
- roundedSize = (initialSize + 7) / 8 * 8;
- }
-
- return roundedSize;
+ return 1;
}
+ @Deprecated
@UnsupportedAppUsage
private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
- double w = options.outWidth;
- double h = options.outHeight;
-
- int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
- (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
- int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
- (int) Math.min(Math.floor(w / minSideLength),
- Math.floor(h / minSideLength));
-
- if (upperBound < lowerBound) {
- // return the larger one when there is no overlapping zone.
- return lowerBound;
- }
-
- if ((maxNumOfPixels == UNCONSTRAINED) &&
- (minSideLength == UNCONSTRAINED)) {
- return 1;
- } else if (minSideLength == UNCONSTRAINED) {
- return lowerBound;
- } else {
- return upperBound;
- }
- }
-
- /**
- * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
- * The image data will be read from specified pfd if it's not null, otherwise
- * a new input stream will be created using specified ContentResolver.
- *
- * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
- * new BitmapFactory.Options will be created if options is null.
- */
- private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
- Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
- BitmapFactory.Options options) {
- Bitmap b = null;
- try {
- if (pfd == null) pfd = makeInputStream(uri, cr);
- if (pfd == null) return null;
- if (options == null) options = new BitmapFactory.Options();
-
- FileDescriptor fd = pfd.getFileDescriptor();
- options.inSampleSize = 1;
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(fd, null, options);
- if (options.mCancel || options.outWidth == -1
- || options.outHeight == -1) {
- return null;
- }
- options.inSampleSize = computeSampleSize(
- options, minSideLength, maxNumOfPixels);
- options.inJustDecodeBounds = false;
-
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- b = BitmapFactory.decodeFileDescriptor(fd, null, options);
- } catch (OutOfMemoryError ex) {
- Log.e(TAG, "Got oom exception ", ex);
- return null;
- } finally {
- closeSilently(pfd);
- }
- return b;
+ return 1;
}
+ @Deprecated
@UnsupportedAppUsage
private static void closeSilently(ParcelFileDescriptor c) {
- if (c == null) return;
- try {
- c.close();
- } catch (Throwable t) {
- // do nothing
- }
+ IoUtils.closeQuietly(c);
}
+ @Deprecated
@UnsupportedAppUsage
private static ParcelFileDescriptor makeInputStream(
Uri uri, ContentResolver cr) {
@@ -371,6 +395,7 @@ public class ThumbnailUtils {
/**
* Transform source Bitmap to targeted width and height.
*/
+ @Deprecated
@UnsupportedAppUsage
private static Bitmap transform(Matrix scaler,
Bitmap source,
@@ -468,14 +493,7 @@ public class ThumbnailUtils {
return b2;
}
- /**
- * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
- * the thumbnail in exif or the full image.
- * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
- * is not null.
- *
- * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
- */
+ @Deprecated
private static class SizedThumbnailBitmap {
public byte[] mThumbnailData;
public Bitmap mBitmap;
@@ -483,81 +501,9 @@ public class ThumbnailUtils {
public int mThumbnailHeight;
}
- /**
- * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
- * The functions returns a SizedThumbnailBitmap,
- * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
- */
+ @Deprecated
@UnsupportedAppUsage
private static void createThumbnailFromEXIF(String filePath, int targetSize,
int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
- if (filePath == null) return;
-
- ExifInterface exif = null;
- byte [] thumbData = null;
- try {
- exif = new ExifInterface(filePath);
- thumbData = exif.getThumbnail();
- } catch (IOException ex) {
- Log.w(TAG, ex);
- }
-
- BitmapFactory.Options fullOptions = new BitmapFactory.Options();
- BitmapFactory.Options exifOptions = new BitmapFactory.Options();
- int exifThumbWidth = 0;
- int fullThumbWidth = 0;
-
- // Compute exifThumbWidth.
- if (thumbData != null) {
- exifOptions.inJustDecodeBounds = true;
- BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
- exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
- exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
- }
-
- // Compute fullThumbWidth.
- fullOptions.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(filePath, fullOptions);
- fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
- fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
-
- // Choose the larger thumbnail as the returning sizedThumbBitmap.
- if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
- int width = exifOptions.outWidth;
- int height = exifOptions.outHeight;
- exifOptions.inJustDecodeBounds = false;
- sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
- thumbData.length, exifOptions);
- if (sizedThumbBitmap.mBitmap != null) {
- sizedThumbBitmap.mThumbnailData = thumbData;
- sizedThumbBitmap.mThumbnailWidth = width;
- sizedThumbBitmap.mThumbnailHeight = height;
- }
- } else {
- fullOptions.inJustDecodeBounds = false;
- sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
- }
- }
-
- private static Bitmap createThumbnailFromMetadataRetriever(
- String filePath, int targetSize, int maxPixels) {
- if (filePath == null) {
- return null;
- }
- Bitmap thumbnail = null;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- retriever.setDataSource(filePath);
- MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
- params.setPreferredConfig(Bitmap.Config.ARGB_8888);
- thumbnail = retriever.getThumbnailImageAtIndex(-1, params, targetSize, maxPixels);
- } catch (RuntimeException ex) {
- // Assume this is a corrupt video file.
- } finally {
- if (retriever != null) {
- retriever.release();
- }
- }
- return thumbnail;
}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e25e6a5e735f..7481fff74765 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -97,9 +97,10 @@ cc_library_shared {
shared_libs: [
"android.hardware.cas@1.0", // for CasManager. VNDK???
"android.hardware.cas.native@1.0", // CasManager. VNDK???
+ "android.hidl.allocator@1.0",
+ "libhidlmemory",
"libbinder",
"libgui", // for VideoFrameScheduler
- "libhidlallocatorutils",
"libhidlbase", // VNDK???
"libpowermanager", // for JWakeLock. to be removed
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5dd01b03274a..76bbce7f0d87 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,7 +39,6 @@
#include "utils/Errors.h" // for status_t
#include "utils/KeyedVector.h"
#include "utils/String8.h"
-#include "android_media_BufferingParams.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
@@ -94,7 +93,6 @@ struct fields_t {
};
static fields_t fields;
-static BufferingParams::fields_t gBufferingParamsFields;
static PlaybackParams::fields_t gPlaybackParamsFields;
static SyncParams::fields_t gSyncParamsFields;
static VolumeShaperHelper::fields_t gVolumeShaperFields;
@@ -370,50 +368,6 @@ android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsu
setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}
-static jobject
-android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- BufferingParams bp;
- BufferingSettings &settings = bp.settings;
- process_media_player_call(
- env, thiz, mp->getBufferingSettings(&settings),
- "java/lang/IllegalStateException", "unexpected error");
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- ALOGV("getBufferingSettings:{%s}", settings.toString().string());
-
- return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static void
-android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
-{
- if (params == NULL) {
- return;
- }
-
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- BufferingParams bp;
- bp.fillFromJobject(env, gBufferingParamsFields, params);
- ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
-
- process_media_player_call(
- env, thiz, mp->setBufferingSettings(bp.settings),
- "java/lang/IllegalStateException", "unexpected error");
-}
-
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
@@ -976,8 +930,6 @@ android_media_MediaPlayer_native_init(JNIEnv *env)
env->DeleteLocalRef(clazz);
- gBufferingParamsFields.init(env);
-
// Modular DRM
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
if (clazz) {
@@ -1426,8 +1378,6 @@ static const JNINativeMethod gMethods[] = {
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
- {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
- {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 8b6009e749ce..7e6a8abbce90 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -820,7 +820,7 @@ android_media_MediaPlayer2_getCurrentPosition(JNIEnv *env, jobject thiz)
}
static jlong
-android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -828,7 +828,7 @@ android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz)
return 0;
}
int64_t msec;
- process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
+ process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL );
ALOGV("getDuration: %lld (msec)", (long long)msec);
return (jlong) msec;
}
@@ -1408,7 +1408,7 @@ static const JNINativeMethod gMethods[] = {
{"native_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo},
{"native_pause", "()V", (void *)android_media_MediaPlayer2_pause},
{"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition},
- {"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration},
+ {"native_getDuration", "(J)J", (void *)android_media_MediaPlayer2_getDuration},
{"native_release", "()V", (void *)android_media_MediaPlayer2_release},
{"native_reset", "()V", (void *)android_media_MediaPlayer2_reset},
{"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
diff --git a/native/android/net.c b/native/android/net.c
index e32b7875b4e7..4cac371f313b 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,8 +84,7 @@ int android_getaddrinfofornetwork(net_handle_t network,
return android_getaddrinfofornet(node, service, hints, netid, 0, res);
}
-int android_res_nquery(net_handle_t network,
- const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
@@ -94,12 +93,11 @@ int android_res_nquery(net_handle_t network,
return resNetworkQuery(netid, dname, ns_class, ns_type);
}
-int android_res_nresult(int fd, int *rcode, unsigned char *answer, int anslen) {
+int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
return resNetworkResult(fd, rcode, answer, anslen);
}
-int android_res_nsend(net_handle_t network,
- const unsigned char *msg, int msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 74d6605a1ffb..9b6ad38545b4 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -69,6 +69,7 @@ android_app {
],
},
resource_dirs: [
+ "res-keyguard",
"res",
],
@@ -80,4 +81,5 @@ android_app {
"com.android.keyguard",
],
+ annotation_processors: ["dagger2-compiler-2.19"],
}
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
new file mode 100644
index 000000000000..f3a2f0f76a08
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
new file mode 100644
index 000000000000..ef0aac27b84e
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16.2l-3.5,-3.5a0.984,0.984 0,0 0,-1.4 0,0.984 0.984,0 0,0 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7a0.984,0.984 0,0 0,0 -1.4,0.984 0.984,0 0,0 -1.4,0L9,16.2z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
new file mode 100644
index 000000000000..b428931670f5
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/car_button_radius"/>
+ <solid android:color="#131315"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/car_button_radius"/>
+ <solid android:color="@color/button_background"/>
+ </shape>
+ </item>
+</selector>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 000000000000..b115a1f3c131
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Pattern" at the top
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_pattern_view"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/car_margin">
+
+ <FrameLayout
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="@dimen/keyguard_pattern_dimension"
+ android:layout_height="@dimen/keyguard_pattern_dimension"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pattern" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
new file mode 100644
index 000000000000..ed88c6235d58
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Car customizations
+ - Added title "Enter your PIN" under the entry field
+ - Put backspace and enter buttons in row 4
+ - PIN pad is on start side while entry field and title are on the end side
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:paddingHorizontal="@dimen/car_margin">
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent">
+
+ <GridLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:columnCount="3">
+
+ <include layout="@layout/num_pad_keys"/>
+ </GridLayout>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.keyguard.PasswordTextView
+ android:id="@+id/pinEntry"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/pin_entry_height"
+ android:gravity="center"
+ app:scaledTextSize="@integer/password_text_view_scale"
+ android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/divider_height"
+ android:background="@android:color/white" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pin" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+ </LinearLayout>
+
+ <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+ keyguard to crash. Instead put them down here where they are out of the way and set their
+ visibility to gone. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
new file mode 100644
index 000000000000..062f7bd8615e
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Car customizations
+ Car has solid black background instead of a transparent one
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:fitsSystemWindows="true">
+
+ <include
+ style="@style/BouncerSecurityContainer"
+ layout="@layout/keyguard_host_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</FrameLayout>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
new file mode 100644
index 000000000000..c2304147982a
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/Keyguard.TextView"
+ android:id="@+id/keyguard_message_area"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:layout_marginBottom="@dimen/car_padding_4"
+ android:textSize="@dimen/car_body2_size" />
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
new file mode 100644
index 000000000000..c7eda3888d7a
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Car customizations
+ The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+ Removing it will cause the keyguard to crash. Hide them instead
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:id="@+id/digit_text"
+ style="@style/Widget.TextView.NumPadKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <!-- The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+ Removing it will cause the keyguard to crash. Hide them instead -->
+ <TextView
+ android:id="@+id/klondike_text"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
new file mode 100644
index 000000000000..e701fdb956f5
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Password" below the password field
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_password_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <!-- Password entry field -->
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical"
+ android:theme="?attr/passwordStyle">
+
+ <EditText
+ android:id="@+id/passwordEntry"
+ android:layout_width="@dimen/password_field_width"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:textSize="@dimen/car_body1_size"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:imeOptions="flagForceAscii|actionDone"
+ android:maxLength="@integer/password_text_view_scale"
+ />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_password" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <ImageView android:id="@+id/switch_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:src="@drawable/ic_lockscreen_ime"
+ android:contentDescription="@string/accessibility_ime_switch_button"
+ android:clickable="true"
+ android:padding="8dp"
+ android:tint="@color/background_protected"
+ android:layout_gravity="end|center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone"
+ />
+
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
new file mode 100644
index 000000000000..00333a8a826e
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Pattern" at the top
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pattern_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ android:gravity="center_horizontal">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pattern" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="@dimen/keyguard_pattern_dimension"
+ android:layout_height="@dimen/keyguard_pattern_dimension"
+ android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+ </FrameLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 000000000000..16622518c36f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Car customizations
+ - Added title "Enter your PIN" under the entry field
+ - Put backspace and enter buttons in row 4
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="@dimen/num_pad_margin_left"
+ android:layout_marginRight="@dimen/num_pad_margin_right"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.keyguard.PasswordTextView
+ android:id="@+id/pinEntry"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/pin_entry_height"
+ android:gravity="center"
+ app:scaledTextSize="@integer/password_text_view_scale"
+ android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/divider_height"
+ android:background="@android:color/white" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pin" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ </LinearLayout>
+
+ <GridLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+ android:columnCount="3">
+
+ <include layout="@layout/num_pad_keys"/>
+ </GridLayout>
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ </LinearLayout>
+
+ <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+ keyguard to crash. Instead put them down here where they are out of the way and set their
+ visibility to gone. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+
+ <include
+ layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
new file mode 100644
index 000000000000..8306cb4a708a
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <!-- Row 1 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key1"
+ style="@style/NumPadKeyButton"
+ app:digit="1" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key2"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="2" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key3"
+ style="@style/NumPadKeyButton"
+ app:digit="3" />
+
+ <!-- Row 2 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key4"
+ style="@style/NumPadKeyButton"
+ app:digit="4" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key5"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="5" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key6"
+ style="@style/NumPadKeyButton"
+ app:digit="6" />
+
+ <!-- Row 3 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key7"
+ style="@style/NumPadKeyButton"
+ app:digit="7" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key8"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="8" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key9"
+ style="@style/NumPadKeyButton"
+ app:digit="9" />
+
+ <!-- Row 4 -->
+ <ImageButton
+ android:id="@+id/delete_button"
+ style="@style/NumPadKeyButton.LastRow"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_backspace"
+ android:clickable="true"
+ android:tint="@android:color/white"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/keyboardview_keycode_delete" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key0"
+ style="@style/NumPadKeyButton.LastRow.MiddleColumn"
+ app:digit="0" />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/NumPadKeyButton.LastRow"
+ android:src="@drawable/ic_done"
+ android:tint="@android:color/white"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/keyboardview_keycode_enter" />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
new file mode 100644
index 000000000000..d055efa25078
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="pin_pattern_pad_margin_vertical">178dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values-land/dimens.xml b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
new file mode 100644
index 000000000000..805a13497f6f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_5</dimen>
+ <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_4</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/colors.xml b/packages/CarSystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 000000000000..e6edbea3f80e
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="button_background">@color/car_dark_blue_grey_600</color>
+ <color name="button_text">@color/car_action1_light</color>
+</resources> \ No newline at end of file
diff --git a/packages/CarSystemUI/res-keyguard/values/dimens.xml b/packages/CarSystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 000000000000..9424dc3870ba
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources>
+ <dimen name="num_pad_margin_left">112dp</dimen>
+ <dimen name="num_pad_margin_right">144dp</dimen>
+ <dimen name="num_pad_key_width">80dp</dimen>
+ <dimen name="num_pad_key_height">80dp</dimen>
+ <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_6</dimen>
+ <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_5</dimen>
+ <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+ <dimen name="divider_height">1dp</dimen>
+ <dimen name="key_enter_margin_top">128dp</dimen>
+ <dimen name="keyguard_pattern_dimension">400dp</dimen>
+ <dimen name="password_field_width">350dp</dimen>
+ <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/integers.xml b/packages/CarSystemUI/res-keyguard/values/integers.xml
new file mode 100644
index 000000000000..bad1346af452
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources>
+ <integer name="password_text_view_scale">40</integer>
+ <integer name="password_max_length">500</integer>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/styles.xml b/packages/CarSystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 000000000000..b39e6e64316e
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+ fit its container since auto wants the icon to be larger. The padding is added to make it
+ so the icon does not press along the edges of the dialog. -->
+ <style name="NumPadKeyButton">
+ <item name="android:layout_width">@dimen/num_pad_key_width</item>
+ <item name="android:layout_height">@dimen/num_pad_key_height</item>
+ <item name="android:layout_marginBottom">@dimen/num_pad_key_margin_bottom</item>
+ <item name="textView">@id/pinEntry</item>
+ </style>
+
+ <style name="NumPadKeyButton.MiddleColumn">
+ <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+ <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+ </style>
+
+ <style name="NumPadKeyButton.LastRow">
+ <item name="android:layout_marginBottom">0dp</item>
+ </style>
+
+ <style name="NumPadKeyButton.LastRow.MiddleColumn">
+ <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+ <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+ </style>
+
+ <style name="KeyguardButton" parent="Widget.Car.Button">
+ <item name="android:background">@drawable/keyguard_button_background</item>
+ <item name="android:textColor">@color/button_text</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+ <!-- Only replaces the text size. -->
+ <item name="android:textSize">@dimen/car_body1_size</item>
+ </style>
+</resources>
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index 1d6728689933..a2a628d7319e 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -25,13 +25,15 @@
android:orientation="vertical"
android:gravity="center">
- <ImageView android:id="@+id/user_avatar"
+ <ImageView
+ android:id="@+id/user_avatar"
android:layout_width="@dimen/car_user_switcher_image_avatar_size"
android:layout_height="@dimen/car_user_switcher_image_avatar_size"
- android:background="@drawable/car_button_ripple_background_light"
+ android:background="?android:attr/selectableItemBackground"
android:gravity="center"/>
- <TextView android:id="@+id/user_name"
+ <TextView
+ android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/car_user_switcher_vertical_spacing_between_name_and_avatar"
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 6cd70d62b4f7..e8c5134cd180 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -19,12 +19,10 @@
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/car_user_switcher_background_color"
android:visibility="gone">
<LinearLayout
android:id="@+id/container"
- android:background="@color/car_user_switcher_background_color"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -38,7 +36,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/car_user_switcher_margin_top"
- android:theme="@style/Theme.Car.Light.List"
+ android:theme="@style/PagedListTheme"
app:verticallyCenterListContent="true"
app:showPagedListViewDivider="false"
app:gutter="both"
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 141b28a9ae28..72ec8d86368d 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -68,7 +68,7 @@
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
+ android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index b67ce15f80b0..052566d67c1b 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -20,7 +20,7 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<LinearLayout
android:id="@id/nav_buttons"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 46e60db0ba4b..4fa877ff37dc 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -20,7 +20,7 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<LinearLayout
diff --git a/packages/CarSystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
index 6f19cfcfa345..bf96c00e3f0d 100644
--- a/packages/CarSystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -35,7 +35,7 @@
android:layout_centerVertical="true"
android:layout_width="@dimen/car_qs_footer_icon_width"
android:layout_height="@dimen/car_qs_footer_icon_height"
- android:background="@drawable/ripple_drawable"
+ android:background="?android:attr/selectableItemBackground"
android:focusable="true">
<ImageView
diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
index dfa48c30b0c8..d923e0fbb20b 100644
--- a/packages/CarSystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -21,8 +21,7 @@
android:layout_height="wrap_content"
android:background="@color/car_qs_background_primary"
android:orientation="vertical"
- android:elevation="4dp"
- android:theme="@android:style/Theme">
+ android:elevation="4dp">
<include layout="@layout/car_status_bar_header"/>
<include layout="@layout/car_qs_footer"/>
@@ -39,7 +38,7 @@
android:id="@+id/user_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:theme="@style/Theme.Car.Light.List"
+ android:theme="@style/PagedListTheme"
app:showPagedListViewDivider="false"
app:gutter="both"
app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 141b28a9ae28..72ec8d86368d 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -68,7 +68,7 @@
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
+ android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 7b3333e63b7a..1dca10a04c43 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -21,7 +21,7 @@
android:id="@+id/car_top_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<RelativeLayout
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 572737f92370..c527711f563f 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -51,7 +51,6 @@
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
- <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.notifications.NotificationsUI</item>
</string-array>
diff --git a/packages/CarSystemUI/res/values/themes.xml b/packages/CarSystemUI/res/values/themes.xml
new file mode 100644
index 000000000000..8a5961ed051b
--- /dev/null
+++ b/packages/CarSystemUI/res/values/themes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<resources>
+ <!--This Theme contains attributes required for components from the car support lib -->
+ <style name="PagedListTheme" parent="Theme.CarSupportWrapper.NoActionBar">
+ </style>
+</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index f57f26db118c..3c0a2973ad40 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -17,25 +17,44 @@
package com.android.systemui;
import android.content.Context;
-import android.util.ArrayMap;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.statusbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.volume.CarVolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogComponent;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
/**
* Class factory to provide car specific SystemUI components.
*/
public class CarSystemUIFactory extends SystemUIFactory {
+ private CarDependencyComponent mCarDependencyComponent;
+
+ @Override
+ protected void init(Context context) {
+ super.init(context);
+ mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
+ .contextHolder(new ContextHolder(context))
+ .build();
+ }
+
+ public CarDependencyComponent getCarDependencyComponent() {
+ return mCarDependencyComponent;
+ }
+
public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -46,12 +65,33 @@ public class CarSystemUIFactory extends SystemUIFactory {
}
@Override
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
- Context context) {
- super.injectDependencies(providers, context);
- providers.put(NotificationEntryManager.class,
- () -> new CarNotificationEntryManager(context));
- providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
- providers.put(HvacController.class, () -> new HvacController(context));
+ public NotificationEntryManager provideNotificationEntryManager(Context context) {
+ return new CarNotificationEntryManager(context);
+ }
+
+ @Override
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new CarNotificationInterruptionStateProvider(context);
+ }
+
+ @Module
+ protected static class ContextHolder {
+ private Context mContext;
+
+ public ContextHolder(Context context) {
+ mContext = context;
+ }
+
+ @Provides
+ public Context provideContext() {
+ return mContext;
+ }
+ }
+
+ @Singleton
+ @Component(modules = ContextHolder.class)
+ public interface CarDependencyComponent {
+ CarFacetButtonController getCarFacetButtonController();
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 0563418e5fb9..323cae0067d8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,7 +18,6 @@ package com.android.systemui.car;
import android.content.Context;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -39,16 +38,4 @@ public class CarNotificationEntryManager extends NotificationEntryManager {
// long click listener.
return null;
}
-
- @Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (!getPresenter().isPresenterFullyCollapsed()) {
- return false;
- }
-
- return super.shouldHeadsUp(entry);
- }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 000000000000..62502ef60e96
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+
+/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
+public class CarNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+ public CarNotificationInterruptionStateProvider(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded. Ensure this by not pinning any
+ // notification if the shade is already opened.
+ if (!getPresenter().isPresenterFullyCollapsed()) {
+ return false;
+ }
+
+ return super.shouldHeadsUp(entry);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index cea4ab0e4992..0a20eaa0b888 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -26,8 +26,9 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
/**
* CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -76,8 +77,9 @@ public class CarFacetButton extends LinearLayout {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
setupIcons(typedArray);
- CarFacetButtonController carFacetButtonController = Dependency.get(
- CarFacetButtonController.class);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
carFacetButtonController.addFacetButton(this);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 56db242f1eb9..7811a1caeb88 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -29,11 +29,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* CarFacetButtons placed on the nav bar are designed to have visual indication that the active
* application on screen is associated with it. This is basically a similar concept to a radio
* button group.
*/
+@Singleton
public class CarFacetButtonController {
protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
@@ -42,6 +46,7 @@ public class CarFacetButtonController {
protected CarFacetButton mSelectedFacetButton;
protected Context mContext;
+ @Inject
public CarFacetButtonController(Context context) {
mContext = context;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5da236ceb211..7028999c159c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -29,9 +29,11 @@ import android.view.WindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
@@ -102,7 +104,9 @@ public class CarStatusBar extends StatusBar implements
mHvacController.connectToCarService();
- mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ mCarFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
if (!mDeviceIsProvisioned) {
@@ -239,7 +243,7 @@ public class CarStatusBar extends StatusBar implements
@Override
protected void makeStatusBarView() {
super.makeStatusBarView();
- mHvacController = Dependency.get(HvacController.class);
+ mHvacController = new HvacController(mContext);
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 730c3e3440ea..a4424260fef5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -52,6 +52,9 @@ public class DrivingStateHelper {
* is idling or moving, {@code false} otherwise.
*/
public boolean isCurrentlyDriving() {
+ if (mDrivingStateManager == null) {
+ return false;
+ }
try {
CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
if (currentState != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index aec31ee4bfdd..30429eda7be7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.car.hvac;
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+
import android.car.Car;
+import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
@@ -167,7 +171,17 @@ public class HvacController {
private void initComponent(TemperatureView view) {
int id = view.getPropertyId();
int zone = view.getAreaId();
+
try {
+ if (mHvacManager != null
+ && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL)) {
+ if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
+ view.setDisplayInFahrenheit(true);
+ }
+
+ }
if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
view.setTemp(Float.NaN);
return;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 507c60f87b1d..17ef3c0204af 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -36,6 +36,7 @@ public class TemperatureTextView extends TextView implements TemperatureView {
private final int mAreaId;
private final int mPropertyId;
private final String mTempFormat;
+ private boolean mDisplayFahrenheit = false;
public TemperatureTextView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -57,9 +58,17 @@ public class TemperatureTextView extends TextView implements TemperatureView {
setText("--");
return;
}
+ if (mDisplayFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
setText(String.format(mTempFormat, temp));
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayFahrenheit) {
+ mDisplayFahrenheit = displayFahrenheit;
+ }
+
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
*/
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
index 7651356f98a3..c17da1848a8f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -23,10 +23,26 @@ public interface TemperatureView {
/**
* Formats the float for display
*
- * @param temp - The current temp or NaN
+ * @param temp - The current temp in Celsius or NaN
*/
void setTemp(float temp);
+ /**
+ * Render the displayed temperature in Fahrenheit
+ *
+ * @param displayFahrenheit - True if temperature should be displayed in Fahrenheit
+ */
+ void setDisplayInFahrenheit(boolean displayFahrenheit);
+
+ /**
+ * Convert the given temperature in Celsius into Fahrenheit
+ *
+ * @param realTemp - The temperature in Celsius
+ * @return Temperature in Fahrenheit.
+ */
+ default float convertToFahrenheit(float realTemp) {
+ return (realTemp * 9f / 5f) + 32;
+ }
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
index 0467bff46e19..76126fcd949c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -99,6 +99,7 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV
private final TemperatureColorStore mColorStore = new TemperatureColorStore();
private final TemperatureBackgroundAnimator mBackgroundAnimator;
private final TemperatureTextAnimator mTextAnimator;
+ boolean mDisplayInFahrenheit = false;
public AnimatedTemperatureView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +194,9 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV
*/
@Override
public void setTemp(float temp) {
+ if (mDisplayInFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
mTextAnimator.setTemp(temp);
if (Float.isNaN(temp)) {
mBackgroundAnimator.hideCircle();
@@ -219,6 +223,11 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV
mBackgroundAnimator.animateOpen();
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayInFahrenheit) {
+ mDisplayInFahrenheit = displayInFahrenheit;
+ }
+
boolean isMinValue(float temp) {
return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
}
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4ebcd0aa..0be71e6d17b9 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
index ab71802102ae..81a63dd80a63 100644
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -17,14 +17,10 @@ package android.ext.services.sms;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.database.Cursor;
import android.database.CursorWindow;
-import android.net.Uri;
import android.os.Bundle;
import android.service.sms.FinancialSmsService;
-import android.util.Log;
-import java.util.ArrayList;
/**
* Service to provide financial apps access to sms messages.
*/
@@ -36,45 +32,6 @@ public class FinancialSmsServiceImpl extends FinancialSmsService {
@Nullable
@Override
public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
- ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
- if (columnNames == null || columnNames.size() <= 0) {
- return null;
- }
-
- Uri inbox = Uri.parse("content://sms/inbox");
-
- try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
- CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
- int messageCount = cursor.getCount();
- if (messageCount > 0 && cursor.moveToFirst()) {
- window.setNumColumns(columnNames.size());
- for (int row = 0; row < messageCount; row++) {
- if (!window.allocRow()) {
- Log.e(TAG, "CursorWindow ran out of memory.");
- return null;
- }
- for (int col = 0; col < columnNames.size(); col++) {
- String columnName = columnNames.get(col);
- int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
- String inboxColumnValue = cursor.getString(inboxColumnIndex);
- boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
- if (!addedToCursorWindow) {
- Log.e(TAG, "Failed to add:"
- + inboxColumnValue
- + ";column:"
- + columnName);
- return null;
- }
- }
- cursor.moveToNext();
- }
- } else {
- Log.w(TAG, "No sms messages.");
- }
- return window;
- } catch (Exception e) {
- Log.e(TAG, "Failed to get sms messages.");
- return null;
- }
+ return null;
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1eb4b7494085..8d04702ea5f6 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -67,7 +67,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
private static final boolean DEBUG = false;
- public static final String AUTHORITY = "com.android.externalstorage.documents";
+ public static final String AUTHORITY = DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
@@ -96,7 +96,8 @@ public class ExternalStorageProvider extends FileSystemProvider {
public boolean reportAvailableBytes = true;
}
- private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+ private static final String ROOT_ID_PRIMARY_EMULATED =
+ DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
private static final String ROOT_ID_HOME = "home";
private StorageManager mStorageManager;
diff --git a/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index e873157dcf34..d2f1c7ab8060 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -3,7 +3,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_PACKAGE_NAME := MtpDocumentsProviderPerfTests
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
index 26e109d40506..4367652230d5 100644
--- a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
@@ -7,7 +7,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.mtp"
android:label="Performance tests for MtpDocumentsProvider." />
</manifest>
diff --git a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
index 36f6fe97f48c..58b9c6728e0d 100644
--- a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
+++ b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
@@ -23,17 +23,15 @@ import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
-import android.system.Os;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import libcore.io.IoUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.Test;
+
+import java.io.IOException;
@RunWith(JUnit4.class)
public class AppFusePerfTest {
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 1e0ff506cb20..a05a219b917b 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -121,7 +121,7 @@
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
<!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
<string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
- <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <!-- Label of a checkbox that allows to keep the data (e.g. files, settings) of the app on uninstall [CHAR LIMIT=none] -->
<string name="uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string>
<!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index e88074ee4b9c..c6dc26370ebc 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -19,7 +19,7 @@ android_test {
libs: ["android.test.runner.stubs"],
static_libs: [
- "android-support-test",
+ "androidx.test.rules",
"ub-uiautomator",
"mockito-target-minus-junit4",
"print-test-util-lib",
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 307cc936a567..fdcaa52be086 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -54,7 +54,7 @@
</application>
<!-- This runs in its own process, hence it instruments itself -->
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.printspooler.outofprocess.tests"
android:label="PrintSpooler Out of Process Test Cases">
</instrumentation>
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index d21a2e49c634..b649e82d10bc 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -24,7 +24,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.printspooler.outofprocess.tests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 7ebf93d8f4ed..61c2f54a2bf9 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,7 +35,6 @@ import android.print.test.services.AddPrintersActivity;
import android.print.test.services.FirstPrintService;
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.filters.LargeTest;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -43,6 +42,8 @@ import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 042808a07f8f..2321790fa960 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -19,6 +19,7 @@ android_library {
"SettingsLibLayoutPreference",
"SettingsLibActionButtonsPreference",
"SettingsLibEntityHeaderWidgets",
+ "SettingsLibBarChartPreference"
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
new file mode 100644
index 000000000000..477e8979b03b
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibBarChartPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/BarChartPreference/AndroidManifest.xml b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
new file mode 100644
index 000000000000..4b9f1ab8d6cc
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
new file mode 100644
index 000000000000..1a4d7b7dee14
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/bar_chart_title"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:textAppearance="@style/BarChart.Text.HeaderTitle"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center|bottom">
+
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view1"
+ style="@style/BarViewStyle"
+ settings:barColor="#FA7B17"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view2"
+ style="@style/BarViewStyle"
+ settings:barColor="#F439A0"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view3"
+ style="@style/BarViewStyle"
+ settings:barColor="#A142F4"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view4"
+ style="@style/BarViewStyle"
+ settings:barColor="#24C1E0"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/bar_chart_details"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
new file mode 100644
index 000000000000..b053317b9de1
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/bar_view"
+ android:layout_width="8dp"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorAccent"/>
+
+ <ImageView
+ android:id="@+id/icon_view"
+ android:layout_width="@dimen/settings_bar_view_icon_size"
+ android:layout_height="@dimen/settings_bar_view_icon_size"
+ android:scaleType="fitCenter"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"/>
+
+ <TextView
+ android:id="@+id/bar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="2dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/BarChart.Text.Title"/>
+
+ <TextView
+ android:id="@+id/bar_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/BarChart.Text.Summary"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/attrs.xml b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
new file mode 100644
index 000000000000..df3eb0a36919
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <declare-styleable name="SettingsBarView">
+ <!-- The color of bar view -->
+ <attr name="barColor" format="color" />
+ </declare-styleable>
+
+</resources>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/dimens.xml b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
new file mode 100644
index 000000000000..7148afa2d62f
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="settings_bar_view_max_height">106dp</dimen>
+ <dimen name="settings_bar_view_icon_size">24dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
new file mode 100644
index 000000000000..647d0800fe82
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="BarViewStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ </style>
+
+ <style name="BarChart.Text"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="BarChart.Text.HeaderTitle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="BarChart.Text.Title">
+ <item name="android:textSize">22sp</item>
+ </style>
+
+ <style name="BarChart.Text.Summary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
new file mode 100644
index 000000000000..d400159984a6
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import java.util.Arrays;
+
+/**
+ * This BarChartPreference shows up to four bar views in this preference at most.
+ *
+ * <p>The following code sample shows a typical use, with an XML layout and code to initialize the
+ * contents of the BarChartPreference:
+ *
+ * <pre>
+ * &lt;com.android.settingslib.widget.BarChartPreference
+ * android:key="bar_chart"/&gt;
+ * </pre>
+ *
+ * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference defined
+ * in the previous XML layout:
+ *
+ * <pre>
+ * BarViewInfo[] viewsInfo = new BarViewInfo [] {
+ * new BarViewInfo(icon, 18, res of summary),
+ * new BarViewInfo(icon, 25, res of summary),
+ * new BarViewInfo(icon, 10, res of summary),
+ * new BarViewInfo(icon, 3, res of summary),
+ * };
+ * </pre>
+ *
+ * <pre>
+ * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
+ *
+ * preference.setBarChartTitleRes(R.string.title_res);
+ * preference.setBarChartDetailsRes(R.string.details_res);
+ * preference.setBarChartDetailsClickListener(v -> doSomething());
+ * preference.setAllBarViewsData(viewsInfo);
+ * </pre>
+ */
+public class BarChartPreference extends Preference {
+
+ private static final String TAG = "BarChartPreference";
+ private static final int MAXIMUM_BAR_VIEWS = 4;
+ private static final int[] BAR_VIEWS = {
+ R.id.bar_view1,
+ R.id.bar_view2,
+ R.id.bar_view3,
+ R.id.bar_view4
+ };
+
+ private int mMaxBarHeight;
+ @StringRes
+ private int mTitleId;
+ @StringRes
+ private int mDetailsId;
+ private BarViewInfo[] mBarViewsInfo;
+ private View.OnClickListener mDetailsOnClickListener;
+
+ public BarChartPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ public BarChartPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ /**
+ * Set the text resource for bar chart title.
+ */
+ public void setBarChartTitle(@StringRes int resId) {
+ mTitleId = resId;
+ notifyChanged();
+ }
+
+ /**
+ * Set the text resource for bar chart details.
+ */
+ public void setBarChartDetails(@StringRes int resId) {
+ mDetailsId = resId;
+ notifyChanged();
+ }
+
+ /**
+ * Register a callback to be invoked when bar chart details view is clicked.
+ */
+ public void setBarChartDetailsClickListener(@Nullable View.OnClickListener clickListener) {
+ mDetailsOnClickListener = clickListener;
+ notifyChanged();
+ }
+
+ /**
+ * Set all bar view information which you'd like to show in preference.
+ *
+ * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
+ */
+ public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
+ mBarViewsInfo = barViewsInfo;
+ // Do a sort in descending order, the first element would have max {@link
+ // BarViewInfo#mBarNumber}
+ Arrays.sort(mBarViewsInfo);
+ calculateAllBarViewHeights();
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+
+ bindChartTitleView(holder);
+ bindChartDetailsView(holder);
+ updateBarChart(holder);
+ }
+
+ private void init() {
+ setSelectable(false);
+ setLayoutResource(R.layout.settings_bar_chart);
+ mMaxBarHeight = getContext().getResources().getDimensionPixelSize(
+ R.dimen.settings_bar_view_max_height);
+ }
+
+ private void bindChartTitleView(PreferenceViewHolder holder) {
+ final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
+ titleView.setText(mTitleId);
+ }
+
+ private void bindChartDetailsView(PreferenceViewHolder holder) {
+ final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
+ detailsView.setText(mDetailsId);
+ detailsView.setOnClickListener(mDetailsOnClickListener);
+ }
+
+ private void updateBarChart(PreferenceViewHolder holder) {
+ for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
+ final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);
+
+ // If there is no bar views data can be shown.
+ if (mBarViewsInfo == null || index >= mBarViewsInfo.length) {
+ barView.setVisibility(View.GONE);
+ continue;
+ }
+ barView.setVisibility(View.VISIBLE);
+ barView.updateView(mBarViewsInfo[index]);
+ }
+ }
+
+ private void calculateAllBarViewHeights() {
+ // Since we sorted this array in advance, the first element must have the max {@link
+ // BarViewInfo#mHeight}.
+ final int maxBarHeight = mBarViewsInfo[0].getHeight();
+ // If the max number of bar view is zero, then we don't calculate the unit for bar height.
+ final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
+
+ for (BarViewInfo barView : mBarViewsInfo) {
+ barView.setNormalizedHeight(barView.getHeight() * unit);
+ }
+ }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
new file mode 100644
index 000000000000..6bf61ae70312
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * {@link View} for a single vertical bar with icon and summary.
+ */
+public class BarView extends LinearLayout {
+
+ private static final String TAG = "BarView";
+
+ private View mBarView;
+ private ImageView mIcon;
+ private TextView mBarTitle;
+ private TextView mBarSummary;
+
+ public BarView(Context context) {
+ super(context);
+ init();
+ }
+
+ public BarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+
+ // Get accent color
+ TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ @ColorInt final int colorAccent = a.getColor(0, 0);
+
+ // Get bar color from layout XML
+ a = context.obtainStyledAttributes(attrs, R.styleable.SettingsBarView);
+ @ColorInt final int barColor = a.getColor(R.styleable.SettingsBarView_barColor,
+ colorAccent);
+ a.recycle();
+
+ mBarView.setBackgroundColor(barColor);
+ }
+
+ /**
+ * Updates the view with a {@link BarViewInfo}.
+ */
+ void updateView(BarViewInfo barViewInfo) {
+ setOnClickListener(barViewInfo.getClickListener());
+ //Set height of bar view
+ mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight();
+ mIcon.setImageDrawable(barViewInfo.getIcon());
+ // For now, we use the bar number as title.
+ mBarTitle.setText(Integer.toString(barViewInfo.getHeight()));
+ mBarSummary.setText(barViewInfo.getSummary());
+ }
+
+ @VisibleForTesting
+ CharSequence getTitle() {
+ return mBarTitle.getText();
+ }
+
+ @VisibleForTesting
+ CharSequence getSummary() {
+ return mBarSummary.getText();
+ }
+
+ private void init() {
+ LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this);
+ setOrientation(LinearLayout.VERTICAL);
+ setGravity(Gravity.CENTER);
+
+ mBarView = findViewById(R.id.bar_view);
+ mIcon = findViewById(R.id.icon_view);
+ mBarTitle = findViewById(R.id.bar_title);
+ mBarSummary = findViewById(R.id.bar_summary);
+ }
+
+ private void setOnClickListner(View.OnClickListener listener) {
+ mBarView.setOnClickListener(listener);
+ }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
new file mode 100644
index 000000000000..409f9ea05889
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.Comparator;
+
+/**
+ * A class responsible for saving bar view information.
+ */
+public class BarViewInfo implements Comparable<BarViewInfo> {
+
+ private final Drawable mIcon;
+ private View.OnClickListener mClickListener;
+ @StringRes
+ private int mSummary;
+ // A number indicates this bar's height. The larger number shows a higher bar view.
+ private int mHeight;
+ // A real height of bar view.
+ private int mNormalizedHeight;
+
+ /**
+ * Construct a BarViewInfo instance.
+ *
+ * @param icon The icon of bar view.
+ * @param barHeight The height of bar view. Larger number shows a higher bar view.
+ * @param summary The string resource id for summary.
+ */
+ public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary) {
+ mIcon = icon;
+ mHeight = barHeight;
+ mSummary = summary;
+ }
+
+ /**
+ * Set a click listener for bar view.
+ */
+ public void setClickListener(@Nullable View.OnClickListener listener) {
+ mClickListener = listener;
+ }
+
+ @Override
+ public int compareTo(BarViewInfo other) {
+ // Descending order
+ return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mHeight)
+ .compare(other, this);
+ }
+
+ void setHeight(@IntRange(from = 0) int height) {
+ mHeight = height;
+ }
+
+ void setSummary(@StringRes int resId) {
+ mSummary = resId;
+ }
+
+ Drawable getIcon() {
+ return mIcon;
+ }
+
+ int getHeight() {
+ return mHeight;
+ }
+
+ View.OnClickListener getClickListener() {
+ return mClickListener;
+ }
+
+ @StringRes
+ int getSummary() {
+ return mSummary;
+ }
+
+ void setNormalizedHeight(@IntRange(from = 0) int barHeight) {
+ mNormalizedHeight = barHeight;
+ }
+
+ int getNormalizedHeight() {
+ return mNormalizedHeight;
+ }
+}
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index a046332eaae8..7e65848de189 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 2b7babd06b47..177ba00c8a31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -321,6 +321,11 @@ public class BluetoothEventManager {
// Dispatch device add callback to show bonded but
// not connected devices in discovery mode
dispatchDeviceAdded(cachedDevice);
+ Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:"
+ + cachedDevice);
+ } else {
+ Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:"
+ + cachedDevice);
}
cachedDevice.setRssi(rssi);
cachedDevice.setJustDiscovered(true);
@@ -463,6 +468,16 @@ public class BluetoothEventManager {
private class AclStateChangedHandler implements Handler {
@Override
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ if (device == null) {
+ Log.w(TAG, "AclStateChangedHandler: device is null");
+ return;
+ }
+
+ // Avoid to notify Settings UI for Hearing Aid sub device.
+ if (mDeviceManager.isSubDevice(device)) {
+ return;
+ }
+
final String action = intent.getAction();
if (action == null) {
Log.w(TAG, "AclStateChangedHandler: action is null");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1bffff753513..e28c894ff8f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -116,8 +116,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
if (BluetoothUtils.D) {
- Log.d(TAG, "onProfileStateChanged: profile " + profile +
- " newProfileState " + newProfileState);
+ Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice
+ + ", newProfileState " + newProfileState);
}
if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF)
{
@@ -570,7 +570,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
if (BluetoothUtils.D) {
- Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
+ Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice);
BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3a6283812e0c..5b4a8b4f259e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -127,6 +127,25 @@ public class CachedBluetoothDeviceManager {
}
/**
+ * Search for existing sub device {@link CachedBluetoothDevice}.
+ *
+ * @param device the address of the Bluetooth device
+ * @return true for found sub device or false.
+ */
+ public synchronized boolean isSubDevice(BluetoothDevice device) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ if (!cachedDevice.getDevice().equals(device)) {
+ // Check sub devices if it exists
+ CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ if (subDevice != null && subDevice.getDevice().equals(device)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
* Hearing Aid Service is connected and the HiSyncId's are now available.
* @param LocalBluetoothProfileManager profileManager
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 9572fb3c629d..20fe495f1afa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -1,6 +1,8 @@
package com.android.settingslib.core;
import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -11,6 +13,8 @@ import androidx.preference.PreferenceScreen;
*/
public abstract class AbstractPreferenceController {
+ private static final String TAG = "AbstractPrefController";
+
protected final Context mContext;
public AbstractPreferenceController(Context context) {
@@ -22,6 +26,10 @@ public abstract class AbstractPreferenceController {
*/
public void displayPreference(PreferenceScreen screen) {
final String prefKey = getPreferenceKey();
+ if (TextUtils.isEmpty(prefKey)) {
+ Log.w(TAG, "Skipping displayPreference because key is empty:" + getClass().getName());
+ return;
+ }
if (isAvailable()) {
setVisible(screen, prefKey, true /* visible */);
if (this instanceof Preference.OnPreferenceChangeListener) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 9699294df587..71778215e079 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -19,9 +19,7 @@ package com.android.settingslib.deviceinfo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -83,15 +81,13 @@ public abstract class AbstractWifiMacAddressPreferenceController
@SuppressLint("HardwareIds")
@Override
protected void updateConnectivity() {
- WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
- final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ String macAddress = null;
+ if (macAddresses != null && macAddresses.length > 0) {
+ macAddress = macAddresses[0];
+ }
- if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
- mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
- } else if (TextUtils.isEmpty(macAddress)
- || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+ if (TextUtils.isEmpty(macAddress)) {
mWifiMacAddress.setSummary(R.string.status_unavailable);
} else {
mWifiMacAddress.setSummary(macAddress);
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index f7b16f8b18db..c8c05a0f0bb6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.os.PowerManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
+import android.text.TextUtils;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -176,4 +177,22 @@ public class BatterySaverUtils {
setAutoBatterySaverTriggerLevel(context, level);
}
}
+
+ /**
+ * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
+ * is selected but no app is configured to actually provide the signal.
+ * @param context a valid context
+ */
+ public static void revertScheduleToNoneIfNeeded(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ boolean providerConfigured = !TextUtils.isEmpty(context.getString(
+ com.android.internal.R.string.config_batterySaverScheduleProvider));
+ if (currentMode == PowerManager.POWER_SAVER_MODE_DYNAMIC && !providerConfigured) {
+ Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
new file mode 100644
index 000000000000..9af06702a696
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.location;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.DateUtils;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationAccesses {
+ private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+ @VisibleForTesting
+ static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+ // Keep last 24 hours of location app information.
+ private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+ @VisibleForTesting
+ static final int[] LOCATION_OPS = new int[]{
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ };
+
+ private final PackageManager mPackageManager;
+ private final Context mContext;
+ private final IconDrawableFactory mDrawableFactory;
+
+ public RecentLocationAccesses(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
+ }
+
+ /**
+ * Fills a list of applications which queried location recently within specified time.
+ * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+ */
+ public List<Access> getAppList() {
+ // Retrieve a location usage list from AppOps
+ AppOpsManager aoManager =
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+ final int appOpsCount = appOps != null ? appOps.size() : 0;
+
+ // Process the AppOps list and generate a preference list.
+ ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
+ final long now = System.currentTimeMillis();
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+
+ for (int i = 0; i < appOpsCount; ++i) {
+ AppOpsManager.PackageOps ops = appOps.get(i);
+ // Don't show the Android System in the list - it's not actionable for the user.
+ // Also don't show apps belonging to background users except managed users.
+ String packageName = ops.getPackageName();
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+ boolean isAndroidOs =
+ (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+ if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+ continue;
+ }
+ Access access = getAccessFromOps(now, ops);
+ if (access != null) {
+ accesses.add(access);
+ }
+ }
+ return accesses;
+ }
+
+ public List<Access> getAppListSorted() {
+ List<Access> accesses = getAppList();
+ // Sort the list of Access by recency. Most recent accesses first.
+ Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() {
+ @Override
+ public int compare(Access access1, Access access2) {
+ return Long.compare(access1.accessFinishTime, access2.accessFinishTime);
+ }
+ }));
+ return accesses;
+ }
+
+ /**
+ * Creates a Access entry for the given PackageOps.
+ *
+ * This method examines the time interval of the PackageOps first. If the PackageOps is older
+ * than the designated interval, this method ignores the PackageOps object and returns null.
+ * When the PackageOps is fresh enough, this method returns a Access object for the package
+ */
+ private Access getAccessFromOps(long now,
+ AppOpsManager.PackageOps ops) {
+ String packageName = ops.getPackageName();
+ List<AppOpsManager.OpEntry> entries = ops.getOps();
+ long locationAccessFinishTime = 0L;
+ // Earliest time for a location access to end and still be shown in list.
+ long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ for (AppOpsManager.OpEntry entry : entries) {
+ locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(),
+ entry.getLastAccessForegroundTime());
+ }
+ // Bail out if the entry is out of date.
+ if (locationAccessFinishTime < recentLocationCutoffTime) {
+ return null;
+ }
+
+ // The package is fresh enough, continue.
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+
+ Access access = null;
+ try {
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId);
+ if (appInfo == null) {
+ Log.w(TAG, "Null application info retrieved for package " + packageName
+ + ", userId " + userId);
+ return null;
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
+ CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+ CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+ if (appLabel.toString().contentEquals(badgedAppLabel)) {
+ // If badged label is not different from original then no need for it as
+ // a separate content description.
+ badgedAppLabel = null;
+ }
+ access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
+ locationAccessFinishTime);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
+ }
+ return access;
+ }
+
+ public static class Access {
+ public final String packageName;
+ public final UserHandle userHandle;
+ public final Drawable icon;
+ public final CharSequence label;
+ public final CharSequence contentDescription;
+ public final long accessFinishTime;
+
+ private Access(String packageName, UserHandle userHandle, Drawable icon,
+ CharSequence label, CharSequence contentDescription,
+ long accessFinishTime) {
+ this.packageName = packageName;
+ this.userHandle = userHandle;
+ this.icon = icon;
+ this.label = label;
+ this.contentDescription = contentDescription;
+ this.accessFinishTime = accessFinishTime;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6c536f080207..b1c4f14b6aaf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -53,7 +53,7 @@ public abstract class MediaDevice {
*
* @return true if the MediaDevice is be connected to transfer, false otherwise.
*/
- protected boolean isConnected() {
+ public boolean isConnected() {
return mIsConnected;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 27b8dfc28448..0dcdaed67cd6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@ package com.android.settingslib.bluetooth;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -154,4 +155,30 @@ public class BluetoothEventManagerTest {
verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_CONNECTED);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 47b12103e772..43b289464e1d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,6 +18,7 @@ package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -334,6 +335,27 @@ public class CachedBluetoothDeviceManagerTest {
}
/**
+ * Test to verify isSubDevice_validSubDevice().
+ */
+ @Test
+ public void isSubDevice_validSubDevice() {
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+ mCachedDeviceManager.addDevice(mDevice1);
+
+ // Both device are not sub device in default value.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+ // Add Device-2 as sub device of Device-1 with same HiSyncId.
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+ mCachedDeviceManager.addDevice(mDevice2);
+
+ // Verify Device-2 is sub device, but Device-1 is not.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ }
+
+ /**
* Test to verify updateHearingAidsDevices().
*/
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 28de1914838f..f695e0c35df6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -35,6 +35,8 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
+ private static final String KEY_PREF = "test_pref";
+
@Mock
private PreferenceScreen mScreen;
@@ -47,9 +49,9 @@ public class AbstractPreferenceControllerTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
- mPreference.setKey(TestPrefController.KEY_PREF);
- when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
- mTestPrefController = new TestPrefController(mContext);
+ mPreference.setKey(KEY_PREF);
+ when(mScreen.findPreference(KEY_PREF)).thenReturn(mPreference);
+ mTestPrefController = new TestPrefController(mContext, KEY_PREF);
}
@Test
@@ -62,15 +64,24 @@ public class AbstractPreferenceControllerTest {
}
@Test
+ public void displayPref_noKey_shouldDoNothing() {
+ mTestPrefController.isAvailable = true;
+
+ mTestPrefController.displayPreference(mScreen);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ @Test
public void setVisible_prefIsVisible_shouldSetToVisible() {
- mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */);
+ mTestPrefController.setVisible(mScreen, KEY_PREF, true /* visible */);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefNotVisible_shouldSetToInvisible() {
- mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */);
+ mTestPrefController.setVisible(mScreen, KEY_PREF, false /* visible */);
assertThat(mPreference.isVisible()).isFalse();
}
@@ -92,13 +103,14 @@ public class AbstractPreferenceControllerTest {
}
private static class TestPrefController extends AbstractPreferenceController {
- private static final String KEY_PREF = "test_pref";
private static final CharSequence TEST_SUMMARY = "Test";
public boolean isAvailable;
+ private final String mPrefKey;
- public TestPrefController(Context context) {
+ TestPrefController(Context context, String key) {
super(context);
+ mPrefKey = key;
}
@Override
@@ -113,7 +125,7 @@ public class AbstractPreferenceControllerTest {
@Override
public String getPreferenceKey() {
- return KEY_PREF;
+ return mPrefKey;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 74e5bf5a8034..1f7f4bc1f6b6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -93,105 +92,23 @@ public class WifiMacAddressPreferenceControllerTest {
}
@Test
- public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(null).when(mWifiManager).getConnectionInfo();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(null).when(mWifiInfo).getMacAddress();
-
+ public void updateConnectivity_null_setMacUnavailable() {
+ doReturn(null).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
-
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getString(R.string.status_unavailable));
}
@Test
- public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
+ public void updateConnectivity_validMac_setValidMac() {
+ final String[] macAddresses = new String[]{TEST_MAC_ADDRESS};
+ doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
- }
-
- @Test
- public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(null).when(mWifiManager).getConnectionInfo();
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(null).when(mWifiInfo).getMacAddress();
- mController.displayPreference(mScreen);
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
- @Test
- public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized));
- }
-
- @Test
- public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
}
private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
new file mode 100644
index 000000000000..d4e74810ea3f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartPreferenceTest {
+
+ private Context mContext;
+ private View mBarChartView;
+ private Drawable mIcon;
+ private BarView mBarView1;
+ private BarView mBarView2;
+ private BarView mBarView3;
+ private BarView mBarView4;
+ private PreferenceViewHolder mHolder;
+ private BarChartPreference mPreference;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mBarChartView = View.inflate(mContext, R.layout.settings_bar_chart, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
+ mPreference = new BarChartPreference(mContext, null /* attrs */);
+ mPreference.setBarChartTitle(R.string.debug_app);
+ mPreference.setBarChartDetails(R.string.debug_app);
+
+
+ mIcon = mContext.getDrawable(R.drawable.ic_menu);
+ mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
+ mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
+ mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
+ mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
+ }
+
+ @Test
+ public void setBarChartTitleRes_setTitleRes_showInBarChartTitle() {
+ final TextView titleView = (TextView) mBarChartView.findViewById(R.id.bar_chart_title);
+
+ mPreference.setBarChartTitle(R.string.debug_app);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+
+ @Test
+ public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
+ final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+ mPreference.setBarChartDetails(R.string.debug_app);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+
+ @Test
+ public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+ final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+ mPreference.setBarChartDetailsClickListener(v -> {
+ });
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(detailsView.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setOneBarViewInfo_showOneBarView() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("10");
+
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setTwoBarViewsInfo_showTwoBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setThreeBarViewsInfo_showThreeBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("5");
+
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setFourBarViewsInfo_showFourBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("5");
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView4.getTitle()).isEqualTo("2");
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setFourBarViewsInfo_barViewWasSortedInDescending() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("50");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("30");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("10");
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView4.getTitle()).isEqualTo("5");
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setValidSummaryRes_barViewShouldShowSummary() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setClickListenerForBarView_barViewAttachClickListener() {
+ final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+ viewInfo.setClickListener(v -> {
+ });
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.hasOnClickListeners()).isTrue();
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5fe08aab93cc..8cfc2a6b158d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -27,6 +27,7 @@
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2e107a06692..8be67d9a7a51 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,8 @@ android_library {
"mockito-target-inline-minus-junit4",
"testables",
"truth-prebuilt",
+ "dagger2-2.19",
+ "jsr330"
],
libs: [
"android.test.runner",
@@ -125,6 +127,7 @@ android_library {
"--extra-packages",
"com.android.keyguard:com.android.systemui",
],
+ annotation_processors: ["dagger2-compiler-2.19"],
}
android_app {
@@ -134,6 +137,7 @@ android_app {
],
platform_apis: true,
+ product_specific: true,
certificate: "platform",
privileged: true,
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
new file mode 100644
index 000000000000..f81e8cce1f91
--- /dev/null
+++ b/packages/SystemUI/docs/dagger.md
@@ -0,0 +1,204 @@
+# Dagger 2 in SystemUI
+*Dagger 2 is a dependency injection framework that compiles annotations to code
+to create dependencies without reflection*
+
+## Recommended reading
+
+Go read about Dagger 2.
+
+TODO: Add some links.
+
+## State of the world
+
+Dagger 2 has been turned on for SystemUI and a early first pass has been taken
+for converting everything in Dependency.java to use Dagger. Since a lot of
+SystemUI depends on Dependency, stubs have been added to Dependency to proxy
+any gets through to the instances provided by dagger, this will allow migration
+of SystemUI through a number of CLs.
+
+### How it works in SystemUI
+
+For the classes that we're using in Dependency and are switching to dagger, the
+equivalent dagger version is using @Singleton and only having one instance.
+To have the single instance span all of SystemUI and be easily accessible for
+other components, there is a single root Component that exists that generates
+these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
+
+```java
+@Singleton
+@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+public interface SystemUIRootComponent {
+ @Singleton
+ Dependency.DependencyInjector createDependency();
+}
+```
+
+The root modules are what provides the global singleton dependencies across
+SystemUI. ContextHolder is just a wrapper that provides a context.
+SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
+variants (like other form factors). DependencyProvider provides or binds any
+remaining depedencies required.
+
+### Adding injection to a new SystemUI object
+
+Anything that depends on any @Singleton provider from SystemUIRootComponent
+should be declared as a Subcomponent of the root component, this requires
+declaring your own interface for generating your own modules or just the
+object you need injected. The subcomponent also needs to be added to
+SystemUIRootComponent in SystemUIFactory so it can be acquired.
+
+```java
+public interface SystemUIRootComponent {
++ @Singleton
++ Dependency.DependencyInjector createDependency();
+}
+
+public class Dependency extends SystemUI {
+ ...
++ @Subcomponent
++ public interface DependencyInjector {
++ Dependency createSystemUI();
++ }
+}
+```
+
+For objects that extend SystemUI and require injection, you can define an
+injector that creates the injected object for you. This other class should
+be referenced in @string/config_systemUIServiceComponents.
+
+```java
+public static class DependencyCreator implements Injector {
+ @Override
+ public SystemUI apply(Context context) {
+ return SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI();
+ }
+}
+```
+
+### Adding a new injectable object
+
+First tag the constructor with @Inject. Also tag it with @Singleton if only one
+instance should be created.
+
+```java
+@Singleton
+public class SomethingController {
+ @Inject
+ public SomethingController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ // context and mainHandler will be automatically populated.
+ }
+}
+```
+
+If you have an interface class and an implementation class, dagger needs to know
+how to map it. The simplest way to do this is to add a provides method to
+DependencyProvider.
+
+```java
+public class DependencyProvider {
+ ...
+ @Singleton
+ @Provide
+ public SomethingController provideSomethingController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new SomethingControllerImpl(context, mainHandler);
+ }
+}
+```
+
+If you need to access this from Dependency#get, then add an adapter to Dependency
+that maps to the instance provided by Dagger. The changes should be similar
+to the following diff.
+
+```java
+public class Dependency {
+ ...
+ @Inject Lazy<SomethingController> mSomethingController;
+ ...
+ public void start() {
+ ...
+ mProviders.put(SomethingController.class, mSomethingController::get);
+ }
+}
+```
+
+### Using injection with Fragments
+
+Fragments are created as part of the FragmentManager, so they need to be
+setup so the manager knows how to create them. To do that, add a method
+to com.android.systemui.fragments.FragmentService$FragmentCreator that
+returns your fragment class. Thats all thats required, once the method
+exists, FragmentService will automatically pick it up and use injection
+whenever your fragment needs to be created.
+
+```java
+public interface FragmentCreator {
++ NavigationBarFragment createNavigationBar();
+}
+```
+
+If you need to create your fragment (i.e. for the add or replace transaction),
+then the FragmentHostManager can do this for you.
+
+```java
+FragmentHostManager.get(view).create(NavigationBarFragment.class);
+```
+
+### Using injection with Views
+
+Generally, you shouldn't need to inject for a view, as the view should
+be relatively self contained and logic that requires injection should be
+moved to a higher level construct such as a Fragment or a top-level SystemUI
+component, see above for how to do injection for both of which.
+
+Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
+lot of code that could benefit from injection and will need to be migrated
+off from Dependency#get uses. Similar to how fragments are injected, the view
+needs to be added to the interface
+com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+
+```java
+public interface ViewInstanceCreator {
++ QuickStatusBarHeader createQsHeader();
+}
+```
+
+Presumably you need to inflate that view from XML (otherwise why do you
+need anything special? see earlier sections about generic injection). To obtain
+an inflater that supports injected objects, call InjectionInflationController#injectable,
+which will wrap the inflater it is passed in one that can create injected
+objects when needed.
+
+```java
+@Override
+public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
+}
+```
+
+There is one other important thing to note about injecting with views. SysUI
+already has a Context in its global dagger component, so if you simply inject
+a Context, you will not get the one that the view should have with proper
+theming. Because of this, always ensure to tag views that have @Inject with
+the @Named view context.
+
+```java
+public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
+ OtherCustomDependency something) {
+ ...
+}
+```
+
+## TODO List
+
+ - Eliminate usages of Depndency#get
+ - Add support for Fragments to handle injection automatically
+ - (this could be through dagger2-android or something custom)
+ - Reduce number of things with @Provide in DependencyProvider (many can be
+ @Inject instead)
+ - Migrate as many remaining DependencyProvider instances to @Bind
+ - Add links in above TODO
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 1a18f6096e25..6135aebb0587 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -62,4 +62,12 @@ public interface ClockPlugin extends Plugin {
* Notifies that the time zone has changed.
*/
default void onTimeZoneChanged(TimeZone timeZone) {}
+
+ /**
+ * Indicates whether the keyguard status area (date) should be shown below
+ * the clock.
+ */
+ default boolean shouldShowStatusArea() {
+ return true;
+ }
}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index ee94aedfe399..22b0ab7dde4e 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -17,6 +17,7 @@
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
-keep class * extends com.android.systemui.SystemUI
+-keep class * implements com.android.systemui.SystemUI$Injector
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
@@ -27,4 +28,10 @@
-keep class com.android.systemui.plugins.** {
*;
}
+-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
+ *;
+}
+-keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
+ *;
+}
-keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 367a9ae28f97..d52866fbd444 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -20,19 +20,31 @@
<!-- This is a view that shows clock information in Keyguard. -->
<com.android.keyguard.KeyguardClockSwitch
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_alignParentTop="true">
- <TextClock
- android:id="@+id/default_clock_view"
- android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal|top">
+ <FrameLayout
+ android:id="@+id/clock_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_alignParentTop="true">
+ <TextClock
+ android:id="@+id/default_clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:letterSpacing="0.03"
+ android:textColor="?attr/wallpaperTextColor"
+ android:singleLine="true"
+ style="@style/widget_big"
+ android:format12Hour="@string/keyguard_widget_12_hours_format"
+ android:format24Hour="@string/keyguard_widget_24_hours_format" />
+ </FrameLayout>
+ <include layout="@layout/keyguard_status_area"
+ android:id="@+id/keyguard_status_area"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:letterSpacing="0.03"
- android:textColor="?attr/wallpaperTextColor"
- android:singleLine="true"
- style="@style/widget_big"
- android:format12Hour="@string/keyguard_widget_12_hours_format"
- android:format24Hour="@string/keyguard_widget_24_hours_format" />
+ android:layout_below="@id/clock_view" />
</com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 7d8a1f5bbbc7..a9ba19d2d393 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -33,21 +33,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <RelativeLayout
+ <include
+ layout="@layout/keyguard_clock_switch"
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top">
- <include layout="@layout/keyguard_clock_switch"
- android:id="@+id/clock_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <include layout="@layout/keyguard_status_area"
- android:id="@+id/keyguard_status_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/clock_view" />
- </RelativeLayout>
+ android:layout_height="wrap_content" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 67ecf6f7b21a..10fea9d50112 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -50,21 +50,11 @@
android:textSize="13sp"
android:text="@*android:string/global_action_logout" />
- <RelativeLayout
+ <include
+ layout="@layout/keyguard_clock_switch"
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top">
- <include layout="@layout/keyguard_clock_switch"
- android:id="@+id/clock_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <include layout="@layout/keyguard_status_area"
- android:id="@+id/keyguard_status_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/clock_view" />
- </RelativeLayout>
+ android:layout_height="wrap_content" />
<TextView
android:id="@+id/owner_info"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ffc7b3cca357..b9966cf33646 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -33,7 +33,7 @@
<item name="android:gravity">center_horizontal|center_vertical</item>
<item name="android:background">@null</item>
<item name="android:textSize">32sp</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">?attr/wallpaperTextColor</item>
<item name="android:paddingBottom">-16dp</item>
</style>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 61efbd5c2248..889db66526b8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -290,7 +290,7 @@
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -318,7 +318,7 @@
<!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
<string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
- <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
</string-array>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
index 74fd13f9564e..01b012d1fc84 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
@@ -14,12 +14,40 @@
package com.android.systemui.shared.plugins;
+import android.annotation.IntDef;
import android.content.ComponentName;
/**
* Enables and disables plugins.
*/
public interface PluginEnabler {
- void setEnabled(ComponentName component, boolean enabled);
+
+ int ENABLED = 0;
+ int DISABLED_MANUALLY = 1;
+ int DISABLED_INVALID_VERSION = 1;
+ int DISABLED_FROM_EXPLICIT_CRASH = 2;
+ int DISABLED_FROM_SYSTEM_CRASH = 3;
+
+ @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH,
+ DISABLED_FROM_SYSTEM_CRASH})
+ @interface DisableReason {
+ }
+
+ /** Enables plugin via the PackageManager. */
+ void setEnabled(ComponentName component);
+
+ /** Disables a plugin via the PackageManager and records the reason for disabling. */
+ void setDisabled(ComponentName component, @DisableReason int reason);
+
+ /** Returns true if the plugin is enabled in the PackageManager. */
boolean isEnabled(ComponentName component);
+
+ /**
+ * Returns the reason that a plugin is disabled, (if it is).
+ *
+ * It should return {@link #ENABLED} if the plugin is turned on.
+ * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown.
+ */
+ @DisableReason
+ int getDisableReason(ComponentName componentName);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 8e7fadb5c7cb..523720d54eec 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -136,7 +136,7 @@ public class PluginInstanceManager<T extends Plugin> {
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (className.startsWith(info.mPackage)) {
- disable(info);
+ disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
disableAny = true;
}
}
@@ -146,12 +146,13 @@ public class PluginInstanceManager<T extends Plugin> {
public boolean disableAll() {
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (int i = 0; i < plugins.size(); i++) {
- disable(plugins.get(i));
+ disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
}
return plugins.size() != 0;
}
- private void disable(PluginInfo info) {
+ private void disable(PluginInfo info,
+ @PluginEnabler.DisableReason int reason) {
// Live by the sword, die by the sword.
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
@@ -162,9 +163,9 @@ public class PluginInstanceManager<T extends Plugin> {
// Don't disable whitelisted plugins as they are a part of the OS.
return;
}
- Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
- mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
- false);
+ ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
+ Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
+ mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
}
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index dc2a9bd5105b..10b5f1c64d85 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -184,6 +184,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
mListening = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(PLUGIN_CHANGED);
filter.addAction(DISABLE_PLUGIN);
@@ -214,12 +215,13 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
// Don't disable whitelisted plugins as they are a part of the OS.
return;
}
- getPluginEnabler().setEnabled(component, false);
+ getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
SystemMessage.NOTE_PLUGIN);
} else {
Uri data = intent.getData();
String pkg = data.getEncodedSchemeSpecificPart();
+ ComponentName componentName = ComponentName.unflattenFromString(pkg);
if (mOneShotPackages.contains(pkg)) {
int icon = mContext.getResources().getIdentifier("tuner", "drawable",
mContext.getPackageName());
@@ -256,6 +258,17 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
Log.v(TAG, "Reloading " + pkg);
}
}
+ if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
+ @PluginEnabler.DisableReason int disableReason =
+ getPluginEnabler().getDisableReason(componentName);
+ if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
+ || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
+ || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
+ Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+ + "updated: " + componentName.flattenToShortString());
+ getPluginEnabler().setEnabled(componentName);
+ }
+ }
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
for (PluginInstanceManager manager : mPluginMap.values()) {
manager.onPackageChange(pkg);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0ec90148c350..22a23a8f8a21 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -7,6 +7,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import android.widget.TextClock;
import androidx.annotation.VisibleForTesting;
@@ -22,7 +23,7 @@ import java.util.TimeZone;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
-public class KeyguardClockSwitch extends FrameLayout {
+public class KeyguardClockSwitch extends RelativeLayout {
/**
* Optional/alternative clock injected via plugin.
*/
@@ -31,6 +32,15 @@ public class KeyguardClockSwitch extends FrameLayout {
* Default clock.
*/
private TextClock mClockView;
+ /**
+ * Frame for default and custom clock.
+ */
+ private FrameLayout mClockFrame;
+ /**
+ * Status area (date and other stuff) shown below the clock. Plugin can decide whether
+ * or not to show it below the alternate clock.
+ */
+ private View mKeyguardStatusArea;
private final PluginListener<ClockPlugin> mClockPluginListener =
new PluginListener<ClockPlugin>() {
@@ -43,11 +53,14 @@ public class KeyguardClockSwitch extends FrameLayout {
// selected clock face. In the future, the user should be able to
// pick a clock face from the available plugins.
mClockPlugin = plugin;
- addView(view, -1,
+ mClockFrame.addView(view, -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
initPluginParams();
mClockView.setVisibility(View.GONE);
+ if (!plugin.shouldShowStatusArea()) {
+ mKeyguardStatusArea.setVisibility(View.GONE);
+ }
}
}
@@ -56,6 +69,7 @@ public class KeyguardClockSwitch extends FrameLayout {
if (Objects.equals(plugin, mClockPlugin)) {
disconnectPlugin();
mClockView.setVisibility(View.VISIBLE);
+ mKeyguardStatusArea.setVisibility(View.VISIBLE);
}
}
};
@@ -72,6 +86,8 @@ public class KeyguardClockSwitch extends FrameLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mClockView = findViewById(R.id.default_clock_view);
+ mClockFrame = findViewById(R.id.clock_view);
+ mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
}
@Override
@@ -185,7 +201,7 @@ public class KeyguardClockSwitch extends FrameLayout {
if (mClockPlugin != null) {
View view = mClockPlugin.getView();
if (view != null) {
- removeView(view);
+ mClockFrame.removeView(view);
}
mClockPlugin = null;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index e051317b96b6..583dac7996ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -28,6 +28,7 @@ import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowManager;
@@ -45,6 +46,7 @@ public class KeyguardDisplayManager {
private final Context mContext;
private boolean mShowing;
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final SparseArray<Presentation> mPresentations = new SparseArray<>();
@@ -86,6 +88,22 @@ public class KeyguardDisplayManager {
mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
}
+ private boolean isKeyguardShowable(Display display) {
+ if (display == null) {
+ if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
+ return false;
+ }
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+ return false;
+ }
+ display.getDisplayInfo(mTmpDisplayInfo);
+ if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
+ return false;
+ }
+ return true;
+ }
/**
* @param display The display to show the presentation on.
* @return {@code true} if a presentation was added.
@@ -93,7 +111,7 @@ public class KeyguardDisplayManager {
* was already there.
*/
private boolean showPresentation(Display display) {
- if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
+ if (!isKeyguardShowable(display)) return false;
if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
final int displayId = display.getDisplayId();
Presentation presentation = mPresentations.get(displayId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c41ef0ede89e..576660431d82 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -24,6 +24,7 @@ import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.ColorInt;
+import android.annotation.StyleRes;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
@@ -444,9 +445,11 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
static class KeyguardSliceButton extends Button implements
ConfigurationController.ConfigurationListener {
+ @StyleRes
+ private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
+
public KeyguardSliceButton(Context context) {
- super(context, null /* attrs */, 0 /* styleAttr */,
- com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
+ super(context, null /* attrs */, 0 /* styleAttr */, sStyleId);
onDensityOrFontScaleChanged();
setEllipsize(TruncateAt.END);
}
@@ -469,6 +472,11 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
}
@Override
+ public void onOverlayChanged() {
+ setTextAppearance(sStyleId);
+ }
+
+ @Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
updatePadding();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 1e9d288bc605..c6f172684686 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -37,7 +37,7 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
import android.widget.GridLayout;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.ColorUtils;
@@ -173,7 +173,7 @@ public class KeyguardStatusView extends GridLayout implements
mLogoutView.setOnClickListener(this::onLogoutClicked);
}
- mClockView = findViewById(R.id.clock_view);
+ mClockView = findViewById(R.id.keyguard_clock_container);
mClockView.setShowCurrentUserTime(true);
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
@@ -205,8 +205,8 @@ public class KeyguardStatusView extends GridLayout implements
* Moves clock, adjusting margins when slice content changes.
*/
private void onSliceContentChanged() {
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+ LinearLayout.LayoutParams layoutParams =
+ (LinearLayout.LayoutParams) mClockView.getLayoutParams();
layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
mClockView.setLayoutParams(layoutParams);
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 38dadd4b961d..874cdccb8794 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -325,6 +325,17 @@ public class BatteryMeterView extends LinearLayout implements
.inflate(R.layout.battery_percentage_view, null);
}
+ /**
+ * Updates percent view by removing old one and reinflating if necessary
+ */
+ public void updatePercentView() {
+ if (mBatteryPercentView != null) {
+ removeView(mBatteryPercentView);
+ mBatteryPercentView = null;
+ }
+ updateShowPercent();
+ }
+
private void updatePercentText() {
if (mBatteryPercentView != null) {
mBatteryPercentView.setText(
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 327ffcd24762..6b4d07a44d34 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -14,20 +14,15 @@
package com.android.systemui;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
-import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
@@ -36,92 +31,89 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+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.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.function.Consumer;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+import dagger.Subcomponent;
+
/**
* Class to handle ugly dependencies throughout sysui until we determine the
* long-term dependency injection solution.
@@ -143,235 +135,309 @@ public class Dependency extends SystemUI {
/**
* Key for getting a background Looper for background work.
*/
- public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>("background_looper");
+ public static final String BG_LOOPER_NAME = "background_looper";
+ /**
+ * Key for getting a background Handler for background work.
+ */
+ public static final String BG_HANDLER_NAME = "background_handler";
+ /**
+ * Key for getting a Handler for receiving time tick broadcasts on.
+ */
+ public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
+ /**
+ * Generic handler on the main thread.
+ */
+ public static final String MAIN_HANDLER_NAME = "main_handler";
+
+ /**
+ * An email address to send memory leak reports to by default.
+ */
+ public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
+
+ /**
+ * Key for getting a background Looper for background work.
+ */
+ public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
/**
* Key for getting a background Handler for background work.
*/
- public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>("background_handler");
+ public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>(BG_HANDLER_NAME);
/**
* Key for getting a Handler for receiving time tick broadcasts on.
*/
public static final DependencyKey<Handler> TIME_TICK_HANDLER =
- new DependencyKey<>("time_tick_handler");
+ new DependencyKey<>(TIME_TICK_HANDLER_NAME);
/**
* Generic handler on the main thread.
*/
- public static final DependencyKey<Handler> MAIN_HANDLER = new DependencyKey<>("main_handler");
+ public static final DependencyKey<Handler> MAIN_HANDLER =
+ new DependencyKey<>(MAIN_HANDLER_NAME);
/**
* An email address to send memory leak reports to by default.
*/
- public static final DependencyKey<String> LEAK_REPORT_EMAIL
- = new DependencyKey<>("leak_report_email");
+ public static final DependencyKey<String> LEAK_REPORT_EMAIL =
+ new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
+ @Inject Lazy<ActivityStarter> mActivityStarter;
+ @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+ @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
+ @Inject Lazy<BluetoothController> mBluetoothController;
+ @Inject Lazy<LocationController> mLocationController;
+ @Inject Lazy<RotationLockController> mRotationLockController;
+ @Inject Lazy<NetworkController> mNetworkController;
+ @Inject Lazy<ZenModeController> mZenModeController;
+ @Inject Lazy<HotspotController> mHotspotController;
+ @Inject Lazy<CastController> mCastController;
+ @Inject Lazy<FlashlightController> mFlashlightController;
+ @Inject Lazy<UserSwitcherController> mUserSwitcherController;
+ @Inject Lazy<UserInfoController> mUserInfoController;
+ @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+ @Inject Lazy<BatteryController> mBatteryController;
+ @Inject Lazy<ColorDisplayController> mColorDisplayController;
+ @Inject Lazy<ManagedProfileController> mManagedProfileController;
+ @Inject Lazy<NextAlarmController> mNextAlarmController;
+ @Inject Lazy<DataSaverController> mDataSaverController;
+ @Inject Lazy<AccessibilityController> mAccessibilityController;
+ @Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
+ @Inject Lazy<PluginManager> mPluginManager;
+ @Inject Lazy<AssistManager> mAssistManager;
+ @Inject Lazy<SecurityController> mSecurityController;
+ @Inject Lazy<LeakDetector> mLeakDetector;
+ @Inject Lazy<LeakReporter> mLeakReporter;
+ @Inject Lazy<GarbageMonitor> mGarbageMonitor;
+ @Inject Lazy<IconLogger> mIconLogger;
+ @Inject Lazy<TunerService> mTunerService;
+ @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
+ @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
+ @Inject Lazy<ConfigurationController> mConfigurationController;
+ @Inject Lazy<StatusBarIconController> mStatusBarIconController;
+ @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
+ @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+ @Inject Lazy<FragmentService> mFragmentService;
+ @Inject Lazy<ExtensionController> mExtensionController;
+ @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
+ @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
+ @Inject Lazy<VolumeDialogController> mVolumeDialogController;
+ @Inject Lazy<MetricsLogger> mMetricsLogger;
+ @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
+ @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
+ @Inject Lazy<TunablePaddingService> mTunablePaddingService;
+ @Inject Lazy<ForegroundServiceController> mForegroundServiceController;
+ @Inject Lazy<UiOffloadThread> mUiOffloadThread;
+ @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
+ @Inject Lazy<LightBarController> mLightBarController;
+ @Inject Lazy<IWindowManager> mIWindowManager;
+ @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+ @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
+ @Inject Lazy<VibratorHelper> mVibratorHelper;
+ @Inject Lazy<IStatusBarService> mIStatusBarService;
+ @Inject Lazy<DisplayMetrics> mDisplayMetrics;
+ @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
+ @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
+ @Inject Lazy<ShadeController> mShadeController;
+ @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
+ @Inject Lazy<InitController> mInitController;
+ @Inject Lazy<AppOpsController> mAppOpsController;
+ @Inject Lazy<DisplayNavigationBarController> mDisplayNavigationBarController;
+ @Inject Lazy<StatusBarStateController> mStatusBarStateController;
+ @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
+ @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
+ @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+ @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
+ @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
+ @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
+ @Inject Lazy<AmbientPulseManager> mAmbientPulseManager;
+ @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
+ @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
+ @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
+ @Inject Lazy<NotificationListener> mNotificationListener;
+ @Inject Lazy<NotificationLogger> mNotificationLogger;
+ @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
+ @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+ @Inject Lazy<NotificationFilter> mNotificationFilter;
+ @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
+ @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
+ @Inject Lazy<SmartReplyController> mSmartReplyController;
+ @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
+ @Inject Lazy<BubbleController> mBubbleController;
+ @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
+ @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+ @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
+ @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
+ @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+ @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
+ @Nullable
+ @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+
+ @Inject
+ public Dependency() {
+ }
+
@Override
public void start() {
// TODO: Think about ways to push these creation rules out of Dependency to cut down
// on imports.
- mProviders.put(TIME_TICK_HANDLER, () -> {
- HandlerThread thread = new HandlerThread("TimeTick");
- thread.start();
- return new Handler(thread.getLooper());
- });
- mProviders.put(BG_LOOPER, () -> {
- HandlerThread thread = new HandlerThread("SysUiBg",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- return thread.getLooper();
- });
- mProviders.put(BG_HANDLER, () -> new Handler(getDependency(BG_LOOPER)));
- mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
- mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
- mProviders.put(ActivityStarterDelegate.class, () ->
- getDependency(ActivityStarter.class));
-
- mProviders.put(AsyncSensorManager.class, () ->
- new AsyncSensorManager(mContext.getSystemService(SensorManager.class),
- getDependency(PluginManager.class)));
+ mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
+ mProviders.put(BG_LOOPER, mBgLooper::get);
+ mProviders.put(BG_HANDLER, mBgHandler::get);
+ mProviders.put(MAIN_HANDLER, mMainHandler::get);
+ mProviders.put(ActivityStarter.class, mActivityStarter::get);
+ mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
- mProviders.put(SensorPrivacyManager.class, () ->
- mContext.getSystemService(SensorPrivacyManager.class));
+ mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
- mProviders.put(BluetoothController.class, () ->
- new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+ mProviders.put(BluetoothController.class, mBluetoothController::get);
+ mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
- mProviders.put(LocationController.class, () ->
- new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+ mProviders.put(LocationController.class, mLocationController::get);
- mProviders.put(RotationLockController.class, () ->
- new RotationLockControllerImpl(mContext));
+ mProviders.put(RotationLockController.class, mRotationLockController::get);
- mProviders.put(NetworkController.class, () ->
- new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
- getDependency(DeviceProvisionedController.class)));
+ mProviders.put(NetworkController.class, mNetworkController::get);
- mProviders.put(ZenModeController.class, () ->
- new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+ mProviders.put(ZenModeController.class, mZenModeController::get);
- mProviders.put(HotspotController.class, () ->
- new HotspotControllerImpl(mContext));
+ mProviders.put(HotspotController.class, mHotspotController::get);
- mProviders.put(CastController.class, () ->
- new CastControllerImpl(mContext));
+ mProviders.put(CastController.class, mCastController::get);
- mProviders.put(FlashlightController.class, () ->
- new FlashlightControllerImpl(mContext));
+ mProviders.put(FlashlightController.class, mFlashlightController::get);
- mProviders.put(KeyguardMonitor.class, () ->
- new KeyguardMonitorImpl(mContext));
+ mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
- mProviders.put(UserSwitcherController.class, () ->
- new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
- getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+ mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
- mProviders.put(UserInfoController.class, () ->
- new UserInfoControllerImpl(mContext));
+ mProviders.put(UserInfoController.class, mUserInfoController::get);
- mProviders.put(BatteryController.class, () ->
- new BatteryControllerImpl(mContext));
+ mProviders.put(BatteryController.class, mBatteryController::get);
- mProviders.put(ColorDisplayController.class, () ->
- new ColorDisplayController(mContext));
+ mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
- mProviders.put(ManagedProfileController.class, () ->
- new ManagedProfileControllerImpl(mContext));
+ mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
- mProviders.put(NextAlarmController.class, () ->
- new NextAlarmControllerImpl(mContext));
+ mProviders.put(NextAlarmController.class, mNextAlarmController::get);
- mProviders.put(DataSaverController.class, () ->
- get(NetworkController.class).getDataSaverController());
+ mProviders.put(DataSaverController.class, mDataSaverController::get);
- mProviders.put(AccessibilityController.class, () ->
- new AccessibilityController(mContext));
+ mProviders.put(AccessibilityController.class, mAccessibilityController::get);
- mProviders.put(DeviceProvisionedController.class, () ->
- new DeviceProvisionedControllerImpl(mContext));
+ mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
- mProviders.put(PluginManager.class, () ->
- new PluginManagerImpl(mContext, new PluginInitializerImpl()));
+ mProviders.put(PluginManager.class, mPluginManager::get);
- mProviders.put(AssistManager.class, () ->
- new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+ mProviders.put(AssistManager.class, mAssistManager::get);
- mProviders.put(SecurityController.class, () ->
- new SecurityControllerImpl(mContext));
+ mProviders.put(SecurityController.class, mSecurityController::get);
- mProviders.put(LeakDetector.class, LeakDetector::create);
+ mProviders.put(LeakDetector.class, mLeakDetector::get);
- mProviders.put(LEAK_REPORT_EMAIL, () -> null);
+ mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
- mProviders.put(LeakReporter.class, () -> new LeakReporter(
- mContext,
- getDependency(LeakDetector.class),
- getDependency(LEAK_REPORT_EMAIL)));
+ mProviders.put(LeakReporter.class, mLeakReporter::get);
- mProviders.put(
- GarbageMonitor.class,
- () ->
- new GarbageMonitor(
- mContext,
- getDependency(BG_LOOPER),
- getDependency(LeakDetector.class),
- getDependency(LeakReporter.class)));
+ mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
- mProviders.put(TunerService.class, () ->
- new TunerServiceImpl(mContext));
+ mProviders.put(TunerService.class, mTunerService::get);
- mProviders.put(StatusBarWindowController.class, () ->
- new StatusBarWindowController(mContext));
+ mProviders.put(StatusBarWindowController.class, mStatusBarWindowController::get);
- mProviders.put(DarkIconDispatcher.class, () ->
- new DarkIconDispatcherImpl(mContext));
+ mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
- mProviders.put(ConfigurationController.class, () ->
- new ConfigurationControllerImpl(mContext));
+ mProviders.put(ConfigurationController.class, mConfigurationController::get);
- mProviders.put(StatusBarIconController.class, () ->
- new StatusBarIconControllerImpl(mContext));
+ mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
- mProviders.put(ScreenLifecycle.class, () ->
- new ScreenLifecycle());
+ mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
- mProviders.put(WakefulnessLifecycle.class, () ->
- new WakefulnessLifecycle());
+ mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
- mProviders.put(FragmentService.class, () ->
- new FragmentService());
+ mProviders.put(FragmentService.class, mFragmentService::get);
- mProviders.put(ExtensionController.class, () ->
- new ExtensionControllerImpl(mContext));
+ mProviders.put(ExtensionController.class, mExtensionController::get);
- mProviders.put(PluginDependencyProvider.class, () ->
- new PluginDependencyProvider(get(PluginManager.class)));
+ mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
- mProviders.put(LocalBluetoothManager.class, () ->
- LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
- UserHandle.ALL));
+ mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
- mProviders.put(VolumeDialogController.class, () ->
- new VolumeDialogControllerImpl(mContext));
+ mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
- mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+ mProviders.put(MetricsLogger.class, mMetricsLogger::get);
- mProviders.put(AccessibilityManagerWrapper.class,
- () -> new AccessibilityManagerWrapper(mContext));
+ mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
- // Creating a new instance will trigger color extraction.
- // Thankfully this only happens once - during boot - and WallpaperManagerService
- // loads colors from cache.
- mProviders.put(SysuiColorExtractor.class, () -> new SysuiColorExtractor(mContext));
+ mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
- mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());
+ mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
- mProviders.put(ForegroundServiceController.class,
- () -> new ForegroundServiceControllerImpl(mContext));
+ mProviders.put(ForegroundServiceController.class, mForegroundServiceController::get);
- mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+ mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
- mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext));
+ mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
- mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext,
- getDependency(BG_LOOPER), getDependency(MetricsLogger.class)));
+ mProviders.put(IconLogger.class, mIconLogger::get);
- mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
+ mProviders.put(LightBarController.class, mLightBarController::get);
- mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
+ mProviders.put(IWindowManager.class, mIWindowManager::get);
- mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+ mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
- mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+ mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
- mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+ mProviders.put(VibratorHelper.class, mVibratorHelper::get);
- mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+ mProviders.put(IStatusBarService.class, mIStatusBarService::get);
- // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
- // anywhere it is needed.
- mProviders.put(DisplayMetrics.class, () -> new DisplayMetrics());
+ mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
- mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
+ mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
- mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
- mProviders.put(ShadeController.class, () ->
- SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+ mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
+ mProviders.put(ShadeController.class, mShadeController::get);
mProviders.put(NotificationRemoteInputManager.Callback.class,
- () -> new StatusBarRemoteInputCallback(mContext));
-
- mProviders.put(InitController.class, InitController::new);
-
- mProviders.put(AppOpsController.class, () ->
- new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
-
- mProviders.put(DisplayNavigationBarController.class, () ->
- new DisplayNavigationBarController(mContext, getDependency(MAIN_HANDLER)));
-
- // Put all dependencies above here so the factory can override them if it wants.
- SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+ mNotificationRemoteInputManagerCallback::get);
+
+ mProviders.put(InitController.class, mInitController::get);
+
+ mProviders.put(AppOpsController.class, mAppOpsController::get);
+
+ mProviders.put(DisplayNavigationBarController.class,
+ mDisplayNavigationBarController::get);
+
+ mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
+ mProviders.put(NotificationLockscreenUserManager.class,
+ mNotificationLockscreenUserManager::get);
+ mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
+ mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+ mProviders.put(NotificationGroupAlertTransferHelper.class,
+ mNotificationGroupAlertTransferHelper::get);
+ mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
+ mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
+ mProviders.put(AmbientPulseManager.class, mAmbientPulseManager::get);
+ mProviders.put(NotificationBlockingHelperManager.class,
+ mNotificationBlockingHelperManager::get);
+ mProviders.put(NotificationRemoteInputManager.class,
+ mNotificationRemoteInputManager::get);
+ mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
+ mProviders.put(NotificationListener.class, mNotificationListener::get);
+ mProviders.put(NotificationLogger.class, mNotificationLogger::get);
+ mProviders.put(NotificationViewHierarchyManager.class,
+ mNotificationViewHierarchyManager::get);
+ mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+ mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+ mProviders.put(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider::get);
+ mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
+ mProviders.put(SmartReplyController.class, mSmartReplyController::get);
+ mProviders.put(RemoteInputQuickSettingsDisabler.class,
+ mRemoteInputQuickSettingsDisabler::get);
+ mProviders.put(BubbleController.class, mBubbleController::get);
+ mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
sDependency = this;
}
@@ -484,4 +550,20 @@ public class Dependency extends SystemUI {
return mDisplayName;
}
}
+
+ @Subcomponent
+ public interface DependencyInjector {
+ void createSystemUI(Dependency dependency);
+ }
+
+ public static class DependencyCreator implements Injector {
+ @Override
+ public SystemUI apply(Context context) {
+ Dependency dependency = new Dependency();
+ SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI(dependency);
+ return dependency;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
new file mode 100644
index 000000000000..b11e189e3a9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.app.ColorDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IconLogger;
+import com.android.systemui.statusbar.policy.IconLoggerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunablePadding;
+import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+ @Singleton
+ @Provides
+ @Named(TIME_TICK_HANDLER_NAME)
+ public Handler provideHandler() {
+ HandlerThread thread = new HandlerThread("TimeTick");
+ thread.start();
+ return new Handler(thread.getLooper());
+ }
+
+ @Singleton
+ @Provides
+ @Named(BG_LOOPER_NAME)
+ public Looper provideBgLooper() {
+ HandlerThread thread = new HandlerThread("SysUiBg",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ return thread.getLooper();
+ }
+
+ @Singleton
+ @Provides
+ @Named(BG_HANDLER_NAME)
+ public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
+ return new Handler(bgLooper);
+ }
+
+ @Singleton
+ @Provides
+ @Named(MAIN_HANDLER_NAME)
+ public Handler provideMainHandler() {
+ return new Handler(Looper.getMainLooper());
+ }
+
+ @Singleton
+ @Provides
+ public ActivityStarter provideActivityStarter() {
+ return new ActivityStarterDelegate();
+ }
+
+ @Singleton
+ @Provides
+ public InitController provideInitController() {
+ return new InitController();
+ }
+
+ @Singleton
+ @Provides
+ public ActivityStarterDelegate provideActivityStarterDelegate(ActivityStarter starter) {
+ return (ActivityStarterDelegate) starter;
+ }
+
+ @Singleton
+ @Provides
+ public AsyncSensorManager provideAsyncSensorManager(Context context, PluginManager manager) {
+ return new AsyncSensorManager(context.getSystemService(SensorManager.class),
+ manager);
+
+ }
+
+ @Singleton
+ @Provides
+ public BluetoothController provideBluetoothController(Context context,
+ @Named(BG_LOOPER_NAME) Looper looper) {
+ return new BluetoothControllerImpl(context, looper);
+
+ }
+
+ @Singleton
+ @Provides
+ public LocationController provideLocationController(Context context,
+ @Named(BG_LOOPER_NAME) Looper bgLooper) {
+ return new LocationControllerImpl(context, bgLooper);
+
+ }
+
+ @Singleton
+ @Provides
+ public RotationLockController provideRotationLockController(Context context) {
+ return new RotationLockControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public NetworkController provideNetworkController(Context context,
+ @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController controller) {
+ return new NetworkControllerImpl(context, bgLooper,
+ controller);
+
+ }
+
+ @Singleton
+ @Provides
+ public ZenModeController provideZenModeController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new ZenModeControllerImpl(context, mainHandler);
+
+ }
+
+ @Singleton
+ @Provides
+ public HotspotController provideHotspotController(Context context) {
+ return new HotspotControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public CastController provideCastController(Context context) {
+ return new CastControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public FlashlightController provideFlashlightController(Context context) {
+ return new FlashlightControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public KeyguardMonitor provideKeyguardMonitor(Context context) {
+ return new KeyguardMonitorImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public UserSwitcherController provideUserSwitcherController(Context context,
+ KeyguardMonitor keyguardMonitor, @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+ ActivityStarter activityStarter) {
+ return new UserSwitcherController(context, keyguardMonitor, mainHandler, activityStarter);
+ }
+
+ @Singleton
+ @Provides
+ public UserInfoController provideUserInfoContrller(Context context) {
+ return new UserInfoControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public BatteryController provideBatteryController(Context context) {
+ return new BatteryControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public ColorDisplayController provideColorDisplayController(Context context) {
+ return new ColorDisplayController(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public ManagedProfileController provideManagedProfileController(Context context) {
+ return new ManagedProfileControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public NextAlarmController provideNextAlarmController(Context context) {
+ return new NextAlarmControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public DataSaverController provideDataSaverController(NetworkController networkController) {
+ return networkController.getDataSaverController();
+ }
+
+ @Singleton
+ @Provides
+ public AccessibilityController provideAccessibilityController(Context context) {
+ return new AccessibilityController(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public DeviceProvisionedController provideDeviceProvisionedController(Context context) {
+ return new DeviceProvisionedControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public PluginManager providePluginManager(Context context) {
+ return new PluginManagerImpl(context, new PluginInitializerImpl());
+
+ }
+
+ @Singleton
+ @Provides
+ public SecurityController provideSecurityController(Context context) {
+ return new SecurityControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public LeakDetector provideLeakDetector() {
+ return LeakDetector.create();
+
+ }
+
+ @Singleton
+ @Provides
+ public LeakReporter provideLeakReporter(Context context, LeakDetector detector,
+ @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String email) {
+ return new LeakReporter(context, detector, email);
+ }
+
+ @Singleton
+ @Provides
+ public GarbageMonitor provideGarbageMonitor(Context context,
+ @Named(BG_LOOPER_NAME) Looper bgLooper, LeakDetector detector, LeakReporter reporter) {
+ return new GarbageMonitor(context, bgLooper, detector, reporter);
+ }
+
+ @Singleton
+ @Provides
+ public TunerService provideTunerService(Context context) {
+ return new TunerServiceImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public StatusBarWindowController provideStatusBarWindowController(Context context) {
+ return new StatusBarWindowController(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public DarkIconDispatcher provideDarkIconDispatcher(Context context) {
+ return new DarkIconDispatcherImpl(context);
+ }
+
+ @Singleton
+ @Provides
+ public ConfigurationController provideConfigurationController(Context context) {
+ return new ConfigurationControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public StatusBarIconController provideStatusBarIconController(Context context) {
+ return new StatusBarIconControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public ScreenLifecycle provideScreenLifecycle() {
+ return new ScreenLifecycle();
+ }
+
+ @Singleton
+ @Provides
+ public WakefulnessLifecycle provideWakefulnessLifecycle() {
+ return new WakefulnessLifecycle();
+ }
+
+ @Singleton
+ @Provides
+ public ExtensionController provideExtensionController(Context context) {
+ return new ExtensionControllerImpl(context);
+ }
+
+ @Singleton
+ @Provides
+ public PluginDependencyProvider providePluginDependency(PluginManager pluginManager) {
+ return new PluginDependencyProvider(pluginManager);
+ }
+
+ @Singleton
+ @Provides
+ public LocalBluetoothManager provideLocalBluetoothController(Context context,
+ @Named(BG_HANDLER_NAME) Handler bgHandler) {
+ return LocalBluetoothManager.create(context, bgHandler,
+ UserHandle.ALL);
+ }
+
+ @Singleton
+ @Provides
+ public VolumeDialogController provideVolumeDialogController(Context context) {
+ return new VolumeDialogControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public MetricsLogger provideMetricsLogger() {
+ return new MetricsLogger();
+
+ }
+
+ @Singleton
+ @Provides
+ public AccessibilityManagerWrapper provideAccessibilityManagerWrapper(Context context) {
+ return new AccessibilityManagerWrapper(context);
+
+ }
+
+ @Singleton
+ @Provides
+ // Creating a new instance will trigger color extraction.
+ // Thankfully this only happens once - during boot - and WallpaperManagerService
+ // loads colors from cache.
+ public SysuiColorExtractor provideSysuiColorExtractor(Context context) {
+ return new SysuiColorExtractor(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public TunablePadding.TunablePaddingService provideTunablePaddingService() {
+ return new TunablePaddingService();
+
+ }
+
+ @Singleton
+ @Provides
+ public ForegroundServiceController provideForegroundService(Context context) {
+ return new ForegroundServiceControllerImpl(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public UiOffloadThread provideUiOffloadThread() {
+ return new UiOffloadThread();
+ }
+
+ @Singleton
+ @Provides
+ public PowerUI.WarningsUI provideWarningsUi(Context context) {
+ return new PowerNotificationWarnings(context);
+ }
+
+ @Singleton
+ @Provides
+ public IconLogger provideIconLogger(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+ MetricsLogger logger) {
+ return new IconLoggerImpl(context, bgLooper, logger);
+ }
+
+ @Singleton
+ @Provides
+ public LightBarController provideLightBarController(Context context) {
+ return new LightBarController(context);
+ }
+
+ @Singleton
+ @Provides
+ public IWindowManager provideIWindowManager() {
+ return WindowManagerGlobal.getWindowManagerService();
+ }
+
+ @Singleton
+ @Provides
+ public OverviewProxyService provideOverviewProxyService(Context context) {
+ return new OverviewProxyService(context);
+ }
+
+ @Singleton
+ @Provides
+ public VibratorHelper provideVibratorHelper(Context context) {
+ return new VibratorHelper(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public IStatusBarService provideIStatusBarService() {
+ return IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ @Singleton
+ @Provides
+ // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+// anywhere it is needed.
+ public DisplayMetrics provideDisplayMetrics() {
+ return new DisplayMetrics();
+
+ }
+
+ @Singleton
+ @Provides
+ public LockscreenGestureLogger provideLockscreenGestureLogger() {
+ return new LockscreenGestureLogger();
+ }
+
+ @Singleton
+ @Provides
+ public ShadeController provideShadeController(Context context) {
+ return SysUiServiceProvider.getComponent(context, StatusBar.class);
+ }
+
+ @Singleton
+ @Provides
+ public NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+ Context context) {
+ return new StatusBarRemoteInputCallback(context);
+
+ }
+
+ @Singleton
+ @Provides
+ public AppOpsController provideAppOpsController(Context context,
+ @Named(BG_LOOPER_NAME) Looper bgLooper) {
+ return new AppOpsControllerImpl(context, bgLooper);
+
+ }
+
+ @Singleton
+ @Provides
+ public DisplayNavigationBarController provideDisplayNavigationBarController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new DisplayNavigationBarController(context, mainHandler);
+ }
+
+ @Singleton
+ @Provides
+ public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+ return context.getSystemService(SensorPrivacyManager.class);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6cbefb..78a12467cd1c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,6 +24,7 @@ import android.os.Bundle;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Map;
+import java.util.function.Function;
public abstract class SystemUI implements SysUiServiceProvider {
public Context mContext;
@@ -61,4 +62,7 @@ public abstract class SystemUI implements SysUiServiceProvider {
n.addExtras(extras);
}
+
+ public interface Injector extends Function<Context, SystemUI> {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 92aa652131ba..1d8a21d13fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -170,7 +170,11 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
Class cls;
try {
cls = Class.forName(clsName);
- mServices[i] = (SystemUI) cls.newInstance();
+ Object o = cls.newInstance();
+ if (o instanceof SystemUI.Injector) {
+ o = ((SystemUI.Injector) o).apply(this);
+ }
+ mServices[i] = (SystemUI) o;
} catch(ClassNotFoundException ex){
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 867c9175d308..384a14c70e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,9 +16,11 @@
package com.android.systemui;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.ViewGroup;
@@ -26,56 +28,57 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
/**
* Class factory to provide customizable SystemUI components.
*/
+@Module
public class SystemUIFactory {
private static final String TAG = "SystemUIFactory";
static SystemUIFactory mFactory;
+ private SystemUIRootComponent mRootComponent;
- public static SystemUIFactory getInstance() {
- return mFactory;
+ public static <T extends SystemUIFactory> T getInstance() {
+ return (T) mFactory;
}
public static void createFromConfig(Context context) {
@@ -88,6 +91,7 @@ public class SystemUIFactory {
Class<?> cls = null;
cls = context.getClassLoader().loadClass(clsName);
mFactory = (SystemUIFactory) cls.newInstance();
+ mFactory.init(context);
} catch (Throwable t) {
Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
throw new RuntimeException(t);
@@ -96,6 +100,18 @@ public class SystemUIFactory {
public SystemUIFactory() {}
+ protected void init(Context context) {
+ mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
+ .systemUIFactory(this)
+ .dependencyProvider(new com.android.systemui.DependencyProvider())
+ .contextHolder(new ContextHolder(context))
+ .build();
+ }
+
+ public SystemUIRootComponent getRootComponent() {
+ return mRootComponent;
+ }
+
public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -137,33 +153,88 @@ public class SystemUIFactory {
return new VolumeDialogComponent(systemUi, context);
}
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+ @Singleton
+ @Provides
+ public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
+ return new KeyguardEnvironmentImpl();
+ }
+
+ @Singleton
+ @Provides
+ public NotificationLockscreenUserManager provideNotificationLockscreenUserManager(
+ Context context) {
+ return new NotificationLockscreenUserManagerImpl(context);
+ }
+
+ @Singleton
+ @Provides
+ public AssistManager provideAssistManager(DeviceProvisionedController controller,
Context context) {
- providers.put(StatusBarStateController.class, StatusBarStateController::new);
- providers.put(NotificationLockscreenUserManager.class,
- () -> new NotificationLockscreenUserManagerImpl(context));
- providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
- providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
- providers.put(NotificationGroupAlertTransferHelper.class,
- NotificationGroupAlertTransferHelper::new);
- providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
- providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
- providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
- providers.put(NotificationBlockingHelperManager.class,
- () -> new NotificationBlockingHelperManager(context));
- providers.put(NotificationRemoteInputManager.class,
- () -> new NotificationRemoteInputManager(context));
- providers.put(SmartReplyConstants.class,
- () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
- providers.put(NotificationListener.class, () -> new NotificationListener(context));
- providers.put(NotificationLogger.class, NotificationLogger::new);
- providers.put(NotificationViewHierarchyManager.class,
- () -> new NotificationViewHierarchyManager(context));
- providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
- providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
- providers.put(SmartReplyController.class, () -> new SmartReplyController());
- providers.put(RemoteInputQuickSettingsDisabler.class,
- () -> new RemoteInputQuickSettingsDisabler(context));
- providers.put(BubbleController.class, () -> new BubbleController(context));
+ return new AssistManager(controller, context);
+ }
+
+ @Singleton
+ @Provides
+ public NotificationEntryManager provideNotificationEntryManager(Context context) {
+ return new NotificationEntryManager(context);
+ }
+
+ @Singleton
+ @Provides
+ public EnhancedEstimates provideEnhancedEstimates(Context context) {
+ return new EnhancedEstimatesImpl();
+ }
+
+ @Singleton
+ @Provides
+ @Named(LEAK_REPORT_EMAIL_NAME)
+ @Nullable
+ public String provideLeakReportEmail() {
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public NotificationListener provideNotificationListener(Context context) {
+ return new NotificationListener(context);
+ }
+
+ @Singleton
+ @Provides
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new NotificationInterruptionStateProvider(context);
+ }
+
+ @Module
+ protected static class ContextHolder {
+ private Context mContext;
+
+ public ContextHolder(Context context) {
+ mContext = context;
+ }
+
+ @Provides
+ public Context provideContext() {
+ return mContext;
+ }
+ }
+
+ @Singleton
+ @Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+ public interface SystemUIRootComponent {
+ @Singleton
+ Dependency.DependencyInjector createDependency();
+
+ /**
+ * FragmentCreator generates all Fragments that need injection.
+ */
+ @Singleton
+ FragmentService.FragmentCreator createFragmentCreator();
+
+ /**
+ * ViewCreator generates all Views that need injection.
+ */
+ InjectionInflationController.ViewCreator createViewCreator();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 906a210b6b31..52d1260b4221 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -39,25 +39,39 @@ import java.util.Set;
* NotificationPresenter to be displayed to the user.
*/
public class AppOpsControllerImpl implements AppOpsController,
- AppOpsManager.OnOpActiveChangedListener {
+ AppOpsManager.OnOpActiveChangedListener,
+ AppOpsManager.OnOpNotedListener {
- private static final long LOCATION_TIME_DELAY_MS = 5000;
+ private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
private final Context mContext;
- protected final AppOpsManager mAppOps;
- private final H mBGHandler;
+ private final AppOpsManager mAppOps;
+ private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+
@GuardedBy("mActiveItems")
private final List<AppOpItem> mActiveItems = new ArrayList<>();
+ @GuardedBy("mNotedItems")
+ private final List<AppOpItem> mNotedItems = new ArrayList<>();
+
+ protected static final int[] OPS;
+ protected static final String[] OPS_STRING = new String[] {
+ AppOpsManager.OPSTR_CAMERA,
+ AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW,
+ AppOpsManager.OPSTR_RECORD_AUDIO,
+ AppOpsManager.OPSTR_COARSE_LOCATION,
+ AppOpsManager.OPSTR_FINE_LOCATION};
- protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION};
+ static {
+ int numOps = OPS_STRING.length;
+ OPS = new int[numOps];
+ for (int i = 0; i < numOps; i++) {
+ OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]);
+ }
+ }
public AppOpsControllerImpl(Context context, Looper bgLooper) {
mContext = context;
@@ -70,11 +84,18 @@ public class AppOpsControllerImpl implements AppOpsController,
}
@VisibleForTesting
+ protected void setBGHandler(H handler) {
+ mBGHandler = handler;
+ }
+
+ @VisibleForTesting
protected void setListening(boolean listening) {
if (listening) {
mAppOps.startWatchingActive(OPS, this);
+ mAppOps.startWatchingNoted(OPS_STRING, this);
} else {
mAppOps.stopWatchingActive(this);
+ mAppOps.stopWatchingNoted(this);
}
}
@@ -124,10 +145,11 @@ public class AppOpsControllerImpl implements AppOpsController,
if (mCallbacks.isEmpty()) setListening(false);
}
- private AppOpItem getAppOpItem(int code, int uid, String packageName) {
- final int itemsQ = mActiveItems.size();
+ private AppOpItem getAppOpItem(List<AppOpItem> appOpList, int code, int uid,
+ String packageName) {
+ final int itemsQ = appOpList.size();
for (int i = 0; i < itemsQ; i++) {
- AppOpItem item = mActiveItems.get(i);
+ AppOpItem item = appOpList.get(i);
if (item.getCode() == code && item.getUid() == uid
&& item.getPackageName().equals(packageName)) {
return item;
@@ -138,39 +160,59 @@ public class AppOpsControllerImpl implements AppOpsController,
private boolean updateActives(int code, int uid, String packageName, boolean active) {
synchronized (mActiveItems) {
- AppOpItem item = getAppOpItem(code, uid, packageName);
+ AppOpItem item = getAppOpItem(mActiveItems, code, uid, packageName);
if (item == null && active) {
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
mActiveItems.add(item);
- if (code == AppOpsManager.OP_COARSE_LOCATION
- || code == AppOpsManager.OP_FINE_LOCATION) {
- mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
- }
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
return true;
} else if (item != null && !active) {
mActiveItems.remove(item);
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
return true;
- } else if (item != null && active
- && (code == AppOpsManager.OP_COARSE_LOCATION
- || code == AppOpsManager.OP_FINE_LOCATION)) {
- mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
- return true;
}
return false;
}
}
+ private void removeNoted(int code, int uid, String packageName) {
+ AppOpItem item;
+ synchronized (mNotedItems) {
+ item = getAppOpItem(mNotedItems, code, uid, packageName);
+ if (item == null) return;
+ mNotedItems.remove(item);
+ if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
+ }
+ notifySuscribers(code, uid, packageName, false);
+ }
+
+ private void addNoted(int code, int uid, String packageName) {
+ AppOpItem item;
+ synchronized (mNotedItems) {
+ item = getAppOpItem(mNotedItems, code, uid, packageName);
+ if (item == null) {
+ item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
+ mNotedItems.add(item);
+ if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
+ }
+ }
+ mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
+ }
+
/**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
* @return List of active AppOps information
*/
public List<AppOpItem> getActiveAppOps() {
+ ArrayList<AppOpItem> active;
synchronized (mActiveItems) {
- return new ArrayList<>(mActiveItems);
+ active = new ArrayList<>(mActiveItems);
+ }
+ synchronized (mNotedItems) {
+ active.addAll(mNotedItems);
}
+ return active;
}
/**
@@ -192,19 +234,45 @@ public class AppOpsControllerImpl implements AppOpsController,
}
}
}
+ synchronized (mNotedItems) {
+ final int numNotedItems = mNotedItems.size();
+ for (int i = 0; i < numNotedItems; i++) {
+ AppOpItem item = mNotedItems.get(i);
+ if (UserHandle.getUserId(item.getUid()) == userId) {
+ list.add(item);
+ }
+ }
+ }
return list;
}
@Override
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
if (updateActives(code, uid, packageName, active)) {
+ notifySuscribers(code, uid, packageName, active);
+ }
+ }
+
+ @Override
+ public void onOpNoted(String code, int uid, String packageName, int result) {
+ if (DEBUG) {
+ Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+ }
+ if (result != AppOpsManager.MODE_ALLOWED) return;
+ int op_code = AppOpsManager.strOpToOp(code);
+ addNoted(op_code, uid, packageName);
+ notifySuscribers(op_code, uid, packageName, true);
+ }
+
+ private void notifySuscribers(int code, int uid, String packageName, boolean active) {
+ if (mCallbacksByCode.containsKey(code)) {
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
}
}
}
- private final class H extends Handler {
+ protected final class H extends Handler {
H(Looper looper) {
super(looper);
}
@@ -214,8 +282,7 @@ public class AppOpsControllerImpl implements AppOpsController,
postDelayed(new Runnable() {
@Override
public void run() {
- onOpActiveChanged(item.getCode(), item.getUid(),
- item.getPackageName(), false);
+ removeNoted(item.getCode(), item.getUid(), item.getPackageName());
}
}, item, timeToRemoval);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1e91ef3f10c7..c8595ebb5fca 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,12 +42,16 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Bubbles are a special type of content that can "float" on top of other apps or System UI.
* Bubbles can be expanded to show more content.
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
+@Singleton
public class BubbleController {
private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
@@ -117,6 +121,7 @@ public class BubbleController {
void onBubbleExpandChanged(boolean isExpanding, float amount);
}
+ @Inject
public BubbleController(Context context) {
mContext = context;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 4fb1bc59a23f..974cd8804841 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@ public class DozeLog {
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final int REASONS = 8;
+ private static final int REASONS = 7;
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -182,7 +182,7 @@ public class DozeLog {
*/
public static void traceLockScreenWakeUp(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp " + wake);
+ log("wakeLockScreen " + wake);
}
/**
@@ -191,7 +191,7 @@ public class DozeLog {
*/
public static void traceWakeDisplay(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp " + wake);
+ log("wakeDisplay " + wake);
}
public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 60e39b21680b..30f6e1affe0a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -41,6 +41,8 @@ import com.android.systemui.util.leak.LeakDetector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -186,6 +188,13 @@ public class FragmentHostManager {
mFragments.dispatchDestroy();
}
+ /**
+ * Creates a fragment that requires injection.
+ */
+ public <T> T create(Class<T> fragmentCls) {
+ return (T) mPlugins.instantiate(mContext, fragmentCls.getName(), null);
+ }
+
public interface FragmentListener {
void onFragmentViewCreated(String tag, Fragment fragment);
@@ -294,13 +303,36 @@ public class FragmentHostManager {
Fragment instantiate(Context context, String className, Bundle arguments) {
Context extensionContext = mExtensionLookup.get(className);
if (extensionContext != null) {
- Fragment f = Fragment.instantiate(extensionContext, className, arguments);
+ Fragment f = instantiateWithInjections(extensionContext, className, arguments);
if (f instanceof Plugin) {
((Plugin) f).onCreate(mContext, extensionContext);
}
return f;
}
- return Fragment.instantiate(context, className, arguments);
+ return instantiateWithInjections(context, className, arguments);
+ }
+
+ private Fragment instantiateWithInjections(Context context, String className,
+ Bundle args) {
+ Method method = mManager.getInjectionMap().get(className);
+ if (method != null) {
+ try {
+ Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator());
+ // Setup the args, taken from Fragment#instantiate.
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.setArguments(args);
+ }
+ return f;
+ } catch (IllegalAccessException e) {
+ throw new Fragment.InstantiationException("Unable to instantiate " + className,
+ e);
+ } catch (InvocationTargetException e) {
+ throw new Fragment.InstantiationException("Unable to instantiate " + className,
+ e);
+ }
+ }
+ return Fragment.instantiate(context, className, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index bf7d629c5d7a..8dbaf0f681cf 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -14,6 +14,7 @@
package com.android.systemui.fragments;
+import android.app.Fragment;
import android.content.res.Configuration;
import android.os.Handler;
import android.util.ArrayMap;
@@ -21,20 +22,56 @@ import android.view.View;
import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.Dumpable;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.statusbar.phone.NavigationBarFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Subcomponent;
/**
* Holds a map of root views to FragmentHostStates and generates them as needed.
* Also dispatches the configuration changes to all current FragmentHostStates.
*/
+@Singleton
public class FragmentService implements ConfigurationChangedReceiver, Dumpable {
private static final String TAG = "FragmentService";
private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
+ private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
private final Handler mHandler = new Handler();
+ private final FragmentCreator mFragmentCreator;
+
+ @Inject
+ public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) {
+ mFragmentCreator = rootComponent.createFragmentCreator();
+ initInjectionMap();
+ }
+
+ ArrayMap<String, Method> getInjectionMap() {
+ return mInjectionMap;
+ }
+
+ FragmentCreator getFragmentCreator() {
+ return mFragmentCreator;
+ }
+
+ private void initInjectionMap() {
+ for (Method method : FragmentCreator.class.getDeclaredMethods()) {
+ if (Fragment.class.isAssignableFrom(method.getReturnType())
+ && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+ mInjectionMap.put(method.getReturnType().getName(), method);
+ }
+ }
+ }
public FragmentHostManager getFragmentHostManager(View view) {
View root = view.getRootView();
@@ -74,6 +111,21 @@ public class FragmentService implements ConfigurationChangedReceiver, Dumpable {
}
}
+ /**
+ * The subcomponent of dagger that holds all fragments that need injection.
+ */
+ @Subcomponent
+ public interface FragmentCreator {
+ /**
+ * Inject a NavigationBarFragment.
+ */
+ NavigationBarFragment createNavigationBarFragment();
+ /**
+ * Inject a QSFragment.
+ */
+ QSFragment createQSFragment();
+ }
+
private class FragmentHostState {
private final View mView;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index e2417f7b6397..63374150adaa 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -16,28 +16,44 @@ package com.android.systemui.plugins;
import android.content.ComponentName;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.shared.plugins.PluginEnabler;
public class PluginEnablerImpl implements PluginEnabler {
+ private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
- final private PackageManager mPm;
+ private PackageManager mPm;
+ private final SharedPreferences mAutoDisabledPrefs;
public PluginEnablerImpl(Context context) {
- this(context.getPackageManager());
+ this(context, context.getPackageManager());
}
- @VisibleForTesting public PluginEnablerImpl(PackageManager pm) {
+ @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
+ mAutoDisabledPrefs = context.getSharedPreferences(
+ CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
mPm = pm;
}
@Override
- public void setEnabled(ComponentName component, boolean enabled) {
+ public void setEnabled(ComponentName component) {
+ setDisabled(component, ENABLED);
+ }
+
+ @Override
+ public void setDisabled(ComponentName component, @DisableReason int reason) {
+ boolean enabled = reason == ENABLED;
final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP);
+ if (enabled) {
+ mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply();
+ } else {
+ mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply();
+ }
}
@Override
@@ -45,4 +61,12 @@ public class PluginEnablerImpl implements PluginEnabler {
return mPm.getComponentEnabledSetting(component)
!= PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
+
+ @Override
+ public @DisableReason int getDisableReason(ComponentName componentName) {
+ if (isEnabled(componentName)) {
+ return ENABLED;
+ }
+ return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 268462ea526e..a87d634451cd 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -115,7 +115,7 @@ class PrivacyItemController(val context: Context, val callback: Callback) {
private fun updatePrivacyList() {
privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
- .mapNotNull { toPrivacyItem(it) }
+ .mapNotNull { toPrivacyItem(it) }.distinct()
}
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 2acbea45a235..93130d4667d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -35,7 +35,6 @@ import android.widget.FrameLayout.LayoutParams;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.R.id;
@@ -46,6 +45,9 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.InjectionInflationController;
+
+import javax.inject.Inject;
public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
private static final String TAG = "QS";
@@ -74,13 +76,21 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
private float mLastQSExpansion = -1;
private boolean mQsDisabled;
- private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
- Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private final InjectionInflationController mInjectionInflater;
+
+ @Inject
+ public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+ InjectionInflationController injectionInflater) {
+ mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
+ mInjectionInflater = injectionInflater;
+ }
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+ inflater = mInjectionInflater.injectable(
+ inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
return inflater.inflate(R.layout.qs_panel, container, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3cecff033c91..f2f83c0e0aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@ package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
@@ -58,7 +60,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* View that contains the top-most bits of the screen (primarily the status bar with date, time, and
* battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -102,6 +106,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
private final Handler mHandler = new Handler();
+ private final BatteryController mBatteryController;
+ private final NextAlarmController mAlarmController;
+ private final ZenModeController mZenController;
+ private final StatusBarIconController mStatusBarIconController;
+ private final ActivityStarter mActivityStarter;
private QSPanel mQsPanel;
@@ -141,8 +150,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private TextView mBatteryRemainingText;
private boolean mShowBatteryPercentAndEstimate;
- private NextAlarmController mAlarmController;
- private ZenModeController mZenController;
private PrivacyItemController mPrivacyItemController;
/** Counts how many times the long press tooltip has been shown to the user. */
private int mShownCount;
@@ -172,10 +179,17 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
};
- public QuickStatusBarHeader(Context context, AttributeSet attrs) {
+ @Inject
+ public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+ NextAlarmController nextAlarmController, ZenModeController zenModeController,
+ BatteryController batteryController, StatusBarIconController statusBarIconController,
+ ActivityStarter activityStarter) {
super(context, attrs);
- mAlarmController = Dependency.get(NextAlarmController.class);
- mZenController = Dependency.get(ZenModeController.class);
+ mAlarmController = nextAlarmController;
+ mZenController = zenModeController;
+ mBatteryController = batteryController;
+ mStatusBarIconController = statusBarIconController;
+ mActivityStarter = activityStarter;
mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
mShownCount = getStoredShownCount();
}
@@ -405,8 +419,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
if (!mShowBatteryPercentAndEstimate) {
return;
}
- mBatteryRemainingText.setText(
- Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+ mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
}
public void setExpanded(boolean expanded) {
@@ -472,7 +485,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+ mStatusBarIconController.addIconGroup(mIconManager);
requestApplyInsets();
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
@@ -515,7 +528,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
- Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+ mStatusBarIconController.removeIconGroup(mIconManager);
mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
super.onDetachedFromWindow();
}
@@ -544,10 +557,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
@Override
public void onClick(View v) {
if (v == mClockView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS),0);
} else if (v == mBatteryMeterView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Intent.ACTION_POWER_USAGE_SUMMARY),0);
} else if (v == mPrivacyChip) {
Handler mUiHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 9d2be39b28fc..4dcacd4bffdc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -87,6 +87,8 @@ public class TileQueryHelper {
if (current != null) {
// The setting QS_TILES is not populated immediately upon Factory Reset
possibleTiles.addAll(Arrays.asList(current.split(",")));
+ } else {
+ current = "";
}
String[] stockSplit = stock.split(",");
for (String spec : stockSplit) {
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 89066651084d..466c8082f0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -183,6 +183,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
public void updateState(Tile tile) {
mTile.setIcon(tile.getIcon());
mTile.setLabel(tile.getLabel());
+ mTile.setSubtitle(tile.getSubtitle());
mTile.setContentDescription(tile.getContentDescription());
mTile.setState(tile.getState());
}
@@ -322,6 +323,14 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
return null;
};
state.label = mTile.getLabel();
+
+ CharSequence subtitle = mTile.getSubtitle();
+ if (subtitle != null && subtitle.length() > 0) {
+ state.secondaryLabel = subtitle;
+ } else {
+ state.secondaryLabel = null;
+ }
+
if (mTile.getContentDescription() != null) {
state.contentDescription = mTile.getContentDescription();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 8821679aadf1..9bfd4ee24ff0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -28,17 +28,22 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manager which handles high priority notifications that should "pulse" in when the device is
* dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
* before automatically dismissing the alert.
*/
+@Singleton
public class AmbientPulseManager extends AlertingNotificationManager {
protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
@VisibleForTesting
protected long mExtensionTime;
+ @Inject
public AmbientPulseManager(@NonNull final Context context) {
Resources resources = context.getResources();
mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f0455481b353..7d80860fca85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -61,10 +61,14 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
*/
+@Singleton
public class NotificationMediaManager implements Dumpable {
private static final String TAG = "NotificationMediaManager";
public static final boolean DEBUG_MEDIA = false;
@@ -157,6 +161,7 @@ public class NotificationMediaManager implements Dumpable {
return mEntryManager;
}
+ @Inject
public NotificationMediaManager(Context context) {
mContext = context;
mMediaSessionManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index a5c0a2d3b4d4..c945afd1cb39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
*/
public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
ActivatableNotificationView.OnActivatedListener,
- NotificationEntryManager.Callback {
+ NotificationRowBinder.BindRowCallback {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
* animations if this returns true.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9391737fe23c..d1556fb33f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -59,12 +59,16 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Class for handling remote input state over a set of notifications. This class handles things
* like keeping notifications temporarily that were cancelled as a response to a remote input
* interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
* and handling clicks on remote views.
*/
+@Singleton
public class NotificationRemoteInputManager implements Dumpable {
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -229,6 +233,7 @@ public class NotificationRemoteInputManager implements Dumpable {
return mShadeController;
}
+ @Inject
public NotificationRemoteInputManager(Context context) {
mContext = context;
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0702f1b9f1fe..017cda741e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -41,6 +41,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Stack;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
* on their group structure. For example, if a notification becomes bundled with another,
@@ -48,6 +51,7 @@ import java.util.Stack;
* tell NotificationListContainer which notifications to display, and inform it of changes to those
* notifications that might affect their display.
*/
+@Singleton
public class NotificationViewHierarchyManager {
private static final String TAG = "NotificationViewHierarchyManager";
@@ -123,12 +127,12 @@ public class NotificationViewHierarchyManager {
return mShadeController;
}
+ @Inject
public NotificationViewHierarchyManager(Context context) {
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
- mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index f5d6904a1543..6f1548d2fb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -27,10 +27,14 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles when smart replies are added to a notification
* and clicked upon.
*/
+@Singleton
public class SmartReplyController {
private IStatusBarService mBarService;
private Set<String> mSendingKeys = new ArraySet<>();
@@ -38,7 +42,7 @@ public class SmartReplyController {
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
-
+ @Inject
public SmartReplyController() {
mBarService = Dependency.get(IStatusBarService.class);
}
@@ -88,10 +92,14 @@ public class SmartReplyController {
return mSendingKeys.contains(key);
}
- public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+ /**
+ * Smart Replies and Actions have been added to the UI.
+ */
+ public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+ int actionCount, boolean generatedByAssistant) {
try {
- mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
- replyCount);
+ mBarService.onNotificationSmartSuggestionsAdded(
+ entry.notification.getKey(), replyCount, actionCount, generatedByAssistant);
} catch (RemoteException e) {
// Nothing to do, system going down
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 3f84416ad575..087b65592b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -35,9 +35,13 @@ import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Comparator;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Tracks and reports on {@link StatusBarState}.
*/
+@Singleton
public class StatusBarStateController implements CallbackController<StateListener> {
private static final String TAG = "SbStateController";
@@ -101,6 +105,10 @@ public class StatusBarStateController implements CallbackController<StateListene
public static final int RANK_STACK_SCROLLER = 2;
public static final int RANK_SHELF = 3;
+ @Inject
+ public StatusBarStateController() {
+ }
+
public int getState() {
return mState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
deleted file mode 100644
index 13e991ba2431..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification;
-
-/**
- * Listener interface for when NotificationEntryManager needs to tell
- * NotificationGroupAlertTransferHelper things. Will eventually grow to be a general-purpose
- * listening interface for the NotificationEntryManager.
- */
-public interface AlertTransferListener {
- /**
- * Called when a new notification is posted. At this point, the notification is "pending": its
- * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
- */
- void onPendingEntryAdded(NotificationData.Entry entry);
-
- /**
- * Called when an existing notification's views are reinflated (usually due to an update being
- * posted to that notification).
- */
- void onEntryReinflated(NotificationData.Entry entry);
-
- /**
- * Called when a notification has been removed (either because the user swiped it away or
- * because the developer retracted it).
- */
- void onEntryRemoved(NotificationData.Entry entry);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
new file mode 100644
index 000000000000..49f1a8d6f248
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.app.Notification;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.DejankUtils;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+/**
+ * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
+ * app ops icon, etc) are handled elsewhere.
+ */
+public final class NotificationClicker implements View.OnClickListener {
+ private static final String TAG = "NotificationClicker";
+
+ private final ShadeController mShadeController;
+ private final BubbleController mBubbleController;
+ private final NotificationActivityStarter mNotificationActivityStarter;
+
+ public NotificationClicker(ShadeController shadeController,
+ BubbleController bubbleController,
+ NotificationActivityStarter notificationActivityStarter) {
+ mShadeController = shadeController;
+ mBubbleController = bubbleController;
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
+ @Override
+ public void onClick(final View v) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+ return;
+ }
+
+ mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ if (sbn == null) {
+ Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+ return;
+ }
+
+ // Check if the notification is displaying the menu, if so slide notification back
+ if (isMenuVisible(row)) {
+ row.animateTranslateNotification(0);
+ return;
+ } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+ row.getNotificationParent().animateTranslateNotification(0);
+ return;
+ } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+ // We never want to open the app directly if the user clicks in between
+ // the notifications.
+ return;
+ }
+
+ // Mark notification for one frame.
+ row.setJustClicked(true);
+ DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+ // If it was a bubble we should close it
+ if (row.getEntry().isBubble()) {
+ mBubbleController.collapseStack();
+ }
+
+ mNotificationActivityStarter.onNotificationClicked(sbn, row);
+ }
+
+ private boolean isMenuVisible(ExpandableNotificationRow row) {
+ return row.getProvider() != null && row.getProvider().isMenuVisible();
+ }
+
+ /**
+ * Attaches the click listener to the row if appropriate.
+ */
+ public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+ row.setOnClickListener(this);
+ } else {
+ row.setOnClickListener(null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ae9f323c2ebf..433a994ac0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -27,20 +27,15 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import android.Manifest;
import android.annotation.NonNull;
-import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -58,17 +53,13 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.PrintWriter;
@@ -83,14 +74,13 @@ import java.util.Objects;
*/
public class NotificationData {
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
/**
* These dependencies are late init-ed
*/
private KeyguardEnvironment mEnvironment;
- private ShadeController mShadeController;
private NotificationMediaManager mMediaManager;
- private ForegroundServiceController mFsc;
- private NotificationLockscreenUserManager mUserManager;
private HeadsUpManager mHeadsUpManager;
@@ -105,6 +95,7 @@ public class NotificationData {
public NotificationChannel channel;
public long lastAudiblyAlertedMs;
public boolean noisy;
+ public boolean ambient;
public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
@@ -119,6 +110,9 @@ public class NotificationData {
@NonNull
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ @VisibleForTesting
+ public int suppressedVisualEffects;
+ public boolean suspended;
private Entry parent; // our parent (if we're in a group)
private ArrayList<Entry> children = new ArrayList<Entry>();
@@ -174,6 +168,7 @@ public class NotificationData {
channel = ranking.getChannel();
lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
importance = ranking.getImportance();
+ ambient = ranking.isAmbient();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
systemGeneratedSmartActions = ranking.getSmartActions() == null
@@ -181,6 +176,8 @@ public class NotificationData {
smartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
: ranking.getSmartReplies().toArray(new CharSequence[0]);
+ suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ suspended = ranking.isSuspended();
}
public void setInterruption() {
@@ -623,6 +620,71 @@ public class NotificationData {
if (row == null) return true;
return row.canViewBeDismissed();
}
+
+ boolean isExemptFromDndVisualSuppression() {
+ if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ return false;
+ }
+
+ if ((notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
+ }
+ if (notification.getNotification().isMediaNotification()) {
+ return true;
+ }
+ if (mIsSystemNotification != null && mIsSystemNotification) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldSuppressVisualEffect(int effect) {
+ if (isExemptFromDndVisualSuppression()) {
+ return false;
+ }
+ return (suppressedVisualEffects & effect) != 0;
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressFullScreenIntent() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressPeek() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressStatusBar() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressAmbient() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressNotificationList() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -704,13 +766,6 @@ public class NotificationData {
return mEnvironment;
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
private NotificationMediaManager getMediaManager() {
if (mMediaManager == null) {
mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -718,20 +773,6 @@ public class NotificationData {
return mMediaManager;
}
- private ForegroundServiceController getFsc() {
- if (mFsc == null) {
- mFsc = Dependency.get(ForegroundServiceController.class);
- }
- return mFsc;
- }
-
- private NotificationLockscreenUserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- }
- return mUserManager;
- }
-
/**
* Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
*
@@ -776,7 +817,7 @@ public class NotificationData {
}
public Entry remove(String key, RankingMap ranking) {
- Entry removed = null;
+ Entry removed;
synchronized (mEntries) {
removed = mEntries.remove(key);
}
@@ -847,62 +888,12 @@ public class NotificationData {
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
- }
-
- public boolean shouldSuppressPeek(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
- }
-
- public boolean shouldSuppressStatusBar(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
- }
-
- public boolean shouldSuppressAmbient(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
- }
-
- public boolean shouldSuppressNotificationList(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
- }
-
- private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
- if (isExemptFromDndVisualSuppression(entry)) {
- return false;
- }
- String key = entry.key;
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
- }
- return false;
- }
-
- protected boolean isExemptFromDndVisualSuppression(Entry entry) {
- if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
- return false;
- }
-
- if ((entry.notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return true;
- }
- if (entry.notification.getNotification().isMediaNotification()) {
- return true;
- }
- if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
- return true;
- }
- return false;
- }
-
/**
* Categories that are explicitly called out on DND settings screens are always blocked, if
* DND has flagged them, even if they are foreground or system notifications that might
* otherwise visually bypass DND.
*/
- protected boolean isNotificationBlockedByPolicy(Notification n) {
+ private static boolean isNotificationBlockedByPolicy(Notification n) {
if (isCategory(CATEGORY_CALL, n)
|| isCategory(CATEGORY_MESSAGE, n)
|| isCategory(CATEGORY_ALARM, n)
@@ -913,7 +904,7 @@ public class NotificationData {
return false;
}
- private boolean isCategory(String category, Notification n) {
+ private static boolean isCategory(String category, Notification n) {
return Objects.equals(n.category, category);
}
@@ -1011,7 +1002,7 @@ public class NotificationData {
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
- if (shouldFilterOut(entry)) {
+ if (mNotificationFilter.shouldFilterOut(entry)) {
continue;
}
@@ -1022,87 +1013,6 @@ public class NotificationData {
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
- /**
- * @return true if this notification should NOT be shown right now
- */
- public boolean shouldFilterOut(Entry entry) {
- final StatusBarNotification sbn = entry.notification;
- if (!(getEnvironment().isDeviceProvisioned() ||
- showNotificationEvenIfUnprovisioned(sbn))) {
- return true;
- }
-
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- return true;
- }
-
- if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
- (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || getUserManager().shouldHideNotifications(sbn.getUserId())
- || getUserManager().shouldHideNotifications(sbn.getKey()))) {
- return true;
- }
-
- if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
- return true;
- }
-
- if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
- return true;
- }
-
- if (shouldHide(sbn.getKey())) {
- return true;
- }
-
- if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
- && mGroupManager.isChildInGroupWithSummary(sbn)) {
- return true;
- }
-
- if (getFsc().isDungeonNotification(sbn)
- && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
- // this is a foreground-service disclosure for a user that does not need to show one
- return true;
- }
- if (getFsc().isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Q: What kinds of notifications should show during setup?
- // A: Almost none! Only things coming from packages with permission
- // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
- // as relevant for setup (see below).
- public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
- }
-
- @VisibleForTesting
- static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
- StatusBarNotification sbn) {
- return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
- sbn.getUid()) == PackageManager.PERMISSION_GRANTED
- && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
- }
-
- private static int checkUidPermission(IPackageManager packageManager, String permission,
- int uid) {
- try {
- return packageManager.checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public void dump(PrintWriter pw, String indent) {
int N = mSortedAndFiltered.size();
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
new file mode 100644
index 000000000000..361ae8b1920a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Listener interface for changes sent by NotificationEntryManager.
+ */
+public interface NotificationEntryListener {
+ /**
+ * Called when a new notification is posted. At this point, the notification is "pending": its
+ * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
+ */
+ default void onPendingEntryAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a new entry is created.
+ */
+ default void onNotificationAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was updated.
+ */
+ default void onNotificationUpdated(StatusBarNotification notification) {
+ }
+
+ /**
+ * Called when an existing notification's views are reinflated (usually due to an update being
+ * posted to that notification).
+ */
+ default void onEntryReinflated(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification has been removed (either because the user swiped it away or
+ * because the developer retracted it).
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onEntryRemoved(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was removed.
+ *
+ * @param key key of notification that was removed
+ * @param old StatusBarNotification of the notification before it was removed
+ */
+ default void onNotificationRemoved(String key, StatusBarNotification old) {
+ }
+
+ /**
+ * Removes a notification immediately.
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onPerformRemoveNotification(StatusBarNotification statusBarNotification) {
+ }
+}
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 aab3fc4c7a0e..98ddd6b9cc5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -16,76 +16,49 @@
package com.android.systemui.statusbar.notification;
import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.EventLogTags;
import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -102,122 +75,58 @@ import java.util.concurrent.TimeUnit;
* It also handles tasks such as their inflation and their interaction with other
* Notification.*Manager objects.
*/
-public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
- ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
- VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
+public class NotificationEntryManager implements
+ Dumpable,
+ NotificationInflater.InflationCallback,
+ NotificationUpdateHandler,
+ VisualStabilityManager.Callback,
+ BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean ENABLE_HEADS_UP = true;
- private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
- private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
- private final NotificationClicker mNotificationClicker = new NotificationClicker();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
private final VisualStabilityManager mVisualStabilityManager =
Dependency.get(VisualStabilityManager.class);
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final ForegroundServiceController mForegroundServiceController =
Dependency.get(ForegroundServiceController.class);
private final AmbientPulseManager mAmbientPulseManager =
Dependency.get(AmbientPulseManager.class);
private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
// Lazily retrieved dependencies
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationMediaManager mMediaManager;
private NotificationListener mNotificationListener;
private ShadeController mShadeController;
+ private NotificationRowBinder mNotificationRowBinder;
private final Handler mDeferredNotificationViewUpdateHandler;
private Runnable mUpdateNotificationViewsCallback;
- protected IDreamManager mDreamManager;
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
- private Callback mCallback;
- private NotificationActivityStarter mNotificationActivityStarter;
+ private NotificationEntryListener mCallback;
protected PowerManager mPowerManager;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- private ContentObserver mHeadsUpObserver;
- protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
- private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
- private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
- @Nullable private AlertTransferListener mAlertTransferListener;
-
- private final class NotificationClicker implements View.OnClickListener {
-
- @Override
- public void onClick(final View v) {
- if (!(v instanceof ExpandableNotificationRow)) {
- Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
- return;
- }
-
- getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v);
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final StatusBarNotification sbn = row.getStatusBarNotification();
- if (sbn == null) {
- Log.e(TAG, "NotificationClicker called on an unclickable notification,");
- return;
- }
-
- // Check if the notification is displaying the menu, if so slide notification back
- if (isMenuVisible(row)) {
- row.animateTranslateNotification(0);
- return;
- } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
- row.getNotificationParent().animateTranslateNotification(0);
- return;
- } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
- // We never want to open the app directly if the user clicks in between
- // the notifications.
- return;
- }
-
- // Mark notification for one frame.
- row.setJustClicked(true);
- DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
- // If it was a bubble we should close it
- if (row.getEntry().isBubble()) {
- mBubbleController.collapseStack();
- }
-
- mNotificationActivityStarter.onNotificationClicked(sbn, row);
- }
-
- private boolean isMenuVisible(ExpandableNotificationRow row) {
- return row.getProvider() != null && row.getProvider().isMenuVisible();
- }
-
- public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
- Notification notification = sbn.getNotification();
- if (notification.contentIntent != null || notification.fullScreenIntent != null) {
- row.setOnClickListener(this);
- } else {
- row.setOnClickListener(null);
- }
- }
- }
+ private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
private final DeviceProvisionedController.DeviceProvisionedListener
mDeviceProvisionedListener =
@@ -228,11 +137,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
};
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
public void destroy() {
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
}
@@ -248,8 +152,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
pw.println(entry.notification);
}
}
- pw.print(" mUseHeadsUp=");
- pw.println(mUseHeadsUp);
}
public NotificationEntryManager(Context context) {
@@ -257,16 +159,14 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mMessagingUtil = new NotificationMessagingUtil(context);
mBubbleController.setDismissListener(this /* bubbleEventListener */);
mNotificationData = new NotificationData();
mDeferredNotificationViewUpdateHandler = new Handler();
}
- public void setAlertTransferListener(AlertTransferListener listener) {
- mAlertTransferListener = listener;
+ /** Adds a {@link NotificationEntryListener}. */
+ public void addNotificationEntryListener(NotificationEntryListener listener) {
+ mNotificationEntryListeners.add(listener);
}
/**
@@ -300,8 +200,15 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
return mShadeController;
}
+ private NotificationRowBinder getRowBinder() {
+ if (mNotificationRowBinder == null) {
+ mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
+ }
+ return mNotificationRowBinder;
+ }
+
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer, Callback callback,
+ NotificationListContainer listContainer, NotificationEntryListener callback,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
@@ -310,36 +217,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mNotificationData.setHeadsUpManager(mHeadsUpManager);
mListContainer = listContainer;
- mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- };
-
- if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
- true,
- mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
- mHeadsUpObserver);
- }
-
mNotificationLifetimeExtenders.add(mHeadsUpManager);
mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
@@ -350,14 +227,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
- mHeadsUpObserver.onChange(true); // set up
- mOnAppOpsClickListener = mGutsManager::openGuts;
- }
-
- public void setNotificationActivityStarter(
- NotificationActivityStarter notificationActivityStarter) {
- mNotificationActivityStarter = notificationActivityStarter;
}
public NotificationData getNotificationData() {
@@ -373,18 +242,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return mGutsManager::openGuts;
- }
-
- @Override
- public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- mUiOffloadThread.submit(() -> {
- try {
- mBarService.onNotificationExpansionChanged(key, userAction, expanded);
- } catch (RemoteException e) {
- // Ignore.
- }
- });
+ return getRowBinder().getNotificationLongClicker();
}
@Override
@@ -392,72 +250,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
updateNotifications();
}
- private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
- if (mPresenter.isDeviceInVrMode()) {
- return true;
- }
-
- return mNotificationData.shouldSuppressFullScreenIntent(entry);
- }
-
- private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
-
- final StatusBarNotification sbn = entry.notification;
- if (entry.rowExists()) {
- entry.reset();
- updateNotification(entry, pmUser, sbn, entry.getRow());
- } else {
- new RowInflaterTask().inflate(mContext, parent, entry,
- row -> {
- bindRow(entry, pmUser, sbn, row);
- updateNotification(entry, pmUser, sbn, row);
- });
- }
- }
-
- private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- row.setExpansionLogger(this, entry.notification.getKey());
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setOnExpandClickListener(mPresenter);
- row.setInflationCallback(this);
- row.setLongPressListener(getNotificationLongClicker());
- mListContainer.bindRow(row);
- getRemoteInputManager().bindRow(row);
-
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- final String pkg = sbn.getPackageName();
- String appname = pkg;
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- appname = String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing
- }
- row.setAppName(appname);
- row.setOnDismissRunnable(() ->
- performRemoveNotification(row.getStatusBarNotification()));
- row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- if (ENABLE_REMOTE_INPUT) {
- row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
- }
-
- row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-
- mCallback.onBindRow(entry, pmUser, sbn, row);
- }
-
public void performRemoveNotification(StatusBarNotification n) {
final int rank = mNotificationData.getRank(n.getKey());
final int count = mNotificationData.getActiveNotifications().size();
@@ -569,7 +361,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
// Mark as seen immediately
setNotificationShown(entry.notification);
@@ -578,7 +370,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
- if (shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
@@ -602,8 +394,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onEntryReinflated(entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryReinflated(entry);
}
}
}
@@ -620,8 +412,10 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
final NotificationData.Entry entry = mNotificationData.get(key);
abortExistingInflation(key);
- if (mAlertTransferListener != null && entry != null) {
- mAlertTransferListener.onEntryRemoved(entry);
+ if (entry != null) {
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryRemoved(entry);
+ }
}
// Attempt to remove notifications from their alert managers (heads up, ambient pulse).
@@ -748,56 +542,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
}
- //TODO: This method associates a row with an entry, but eventually needs to not do that
- protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
- boolean isUpdate = mNotificationData.get(entry.key) != null;
- boolean wasLowPriority = row.isLowPriority();
- row.setIsLowPriority(isLowPriority);
- row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
- // bind the click event to the content area
- mNotificationClicker.register(row, sbn);
-
- // Extract target SDK version.
- try {
- ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
- entry.targetSdk = info.targetSdkVersion;
- } catch (PackageManager.NameNotFoundException ex) {
- Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
- }
- row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
- && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
-
- entry.setRow(row);
- row.setOnActivatedListener(mPresenter);
-
- boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
- mNotificationData.getImportance(sbn.getKey()));
- boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
- && !mPresenter.isPresenterFullyCollapsed();
- row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
- row.setEntry(entry);
-
- if (shouldHeadsUp(entry)) {
- row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
- }
- if (shouldPulse(entry)) {
- row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
- }
- row.setNeedsRedaction(
- Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
- row.inflateViews();
- }
-
- private NotificationData.Entry createNotificationViews(
+ private NotificationData.Entry createNotificationEntry(
StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
- Log.d(TAG, "createNotificationViews(notification=" + sbn + " " + ranking);
+ Log.d(TAG, "createNotificationEntry(notification=" + sbn + " " + ranking);
}
NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
@@ -808,7 +557,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
- inflateViews(entry, mListContainer.getViewParentForNotification(entry));
+ getRowBinder().inflateViews(entry, () -> performRemoveNotification(sbn),
+ mNotificationData.get(entry.key) != null);
return entry;
}
@@ -822,51 +572,15 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
- NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
- boolean isHeadsUped = shouldHeadsUp(shadeEntry);
- if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(shadeEntry)) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
- }
- } else if (mNotificationData.getImportance(key)
- < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: not important enough: "
- + key);
- }
- } else {
- // Stop screensaver if the notification has a fullscreen intent.
- // (like an incoming phone call)
- Dependency.get(UiOffloadThread.class).submit(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
-
- // not immersive & a fullscreen alert should be shown
- if (DEBUG)
- Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- key);
- notification.getNotification().fullScreenIntent.send();
- shadeEntry.notifyFullScreenIntentLaunched();
- mMetricsLogger.count("note_fullscreen", 1);
- } catch (PendingIntent.CanceledException e) {
- }
- }
- }
+ NotificationData.Entry entry = createNotificationEntry(notification, ranking);
abortExistingInflation(key);
mForegroundServiceController.addNotification(notification,
mNotificationData.getImportance(key));
- mPendingNotifications.put(key, shadeEntry);
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onPendingEntryAdded(shadeEntry);
+ mPendingNotifications.put(key, entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onPendingEntryAdded(entry);
}
}
@@ -931,16 +645,19 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mGroupManager.onEntryUpdated(entry, oldNotification);
entry.updateIcons(mContext, notification);
- inflateViews(entry, mListContainer.getViewParentForNotification(entry));
+ getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+ mNotificationData.get(entry.key) != null);
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
if (getShadeController().isDozing()) {
- updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldPulse(entry),
+ alertAgain, mAmbientPulseManager);
} else {
- updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldHeadsUp(entry),
+ alertAgain, mHeadsUpManager);
}
updateNotifications();
@@ -999,26 +716,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationData.Entry entry : entries) {
- NotificationUiAdjustment newAdjustment =
- NotificationUiAdjustment.extractFromNotificationEntry(entry);
-
- if (NotificationUiAdjustment.needReinflate(
- oldAdjustments.get(entry.key), newAdjustment)) {
- if (entry.rowExists()) {
- entry.reset();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.getRow());
- } else {
- // Once the RowInflaterTask is done, it will pick up the updated entry, so
- // no-op here.
- }
- } else if (oldImportances.containsKey(entry.key)
- && entry.importance != oldImportances.get(entry.key)) {
- if (entry.rowExists()) {
- entry.getRow().onNotificationRankingUpdated();
- }
- }
+ getRowBinder().onNotificationRankingUpdated(
+ entry,
+ oldImportances.get(entry.key),
+ oldAdjustments.get(entry.key),
+ NotificationUiAdjustment.extractFromNotificationEntry(entry),
+ mNotificationData.get(entry.key) != null);
}
updateNotifications();
@@ -1036,182 +739,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
}
- public void setStatusBarStateListener(
- NotificationViewHierarchyManager.StatusBarStateListener listener) {
- mStatusBarStateListener = listener;
- }
-
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
- }
- return false;
- }
-
- // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
- // message bubble from the bubble to go through heads up path
- boolean inShade = mStatusBarStateListener != null
- && mStatusBarStateListener.getCurrentState() == SHADE;
- if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: no huns or vr mode");
- }
- return false;
- }
-
- boolean isDreaming = false;
- try {
- isDreaming = mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
- if (!inUse) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
- }
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
- }
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mCallback.canHeadsUp(entry, sbn)) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Whether or not the notification should "pulse" on the user's display when the phone is
- * dozing. This displays the ambient view of the notification.
- *
- * @param entry the entry to check
- * @return true if the entry should ambient pulse, false otherwise
- */
- private boolean shouldPulse(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (!getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
- }
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey())
- < NotificationManager.IMPORTANCE_DEFAULT) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
- }
- return false;
- }
-
- Bundle extras = sbn.getNotification().extras;
- CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
- if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
- }
- return false;
- }
-
- return true;
- }
-
- /**
- * Common checks between heads up alerting and ambient pulse alerting. See
- * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
- * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
- * fail any of these checks should not alert at all.
- *
- * @param entry the entry to check
- * @return true if these checks pass, false if the notification should not alert
- */
- protected boolean canAlertCommon(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
- }
- return false;
- }
-
- // Don't alert notifications that are suppressed due to group alert behavior
- if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: suppressed due to group alert behavior");
- }
- return false;
- }
-
- return true;
- }
-
private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1224,10 +751,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
}
- private boolean isSnoozedPackage(StatusBarNotification sbn) {
- return mHeadsUpManager.isSnoozed(sbn.getPackageName());
- }
-
/**
* Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
* method.
@@ -1261,59 +784,4 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
return mPendingNotifications.values();
}
-
- /**
- * Callback for NotificationEntryManager.
- */
- public interface Callback {
-
- /**
- * Called when a new entry is created.
- *
- * @param shadeEntry entry that was created
- */
- void onNotificationAdded(NotificationData.Entry shadeEntry);
-
- /**
- * Called when a notification was updated.
- *
- * @param notification notification that was updated
- */
- void onNotificationUpdated(StatusBarNotification notification);
-
- /**
- * Called when a notification was removed.
- *
- * @param key key of notification that was removed
- * @param old StatusBarNotification of the notification before it was removed
- */
- void onNotificationRemoved(String key, StatusBarNotification old);
-
- /**
- * Called when a new notification and row is created.
- *
- * @param entry entry for the notification
- * @param pmUser package manager for user
- * @param sbn notification
- * @param row row for the notification
- */
- void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row);
-
- /**
- * Removes a notification immediately.
- *
- * @param statusBarNotification notification that is being removed
- */
- void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
- /**
- * Returns true if NotificationEntryManager can heads up this notification.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return true if the notification can be heads upped
- */
- boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 000000000000..5e99c388655f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+ private final NotificationGroupManager mGroupManager = Dependency.get(
+ NotificationGroupManager.class);
+
+ private NotificationData.KeyguardEnvironment mEnvironment;
+ private ShadeController mShadeController;
+ private ForegroundServiceController mFsc;
+ private NotificationLockscreenUserManager mUserManager;
+
+ @Inject
+ public NotificationFilter() {}
+
+ private NotificationData.KeyguardEnvironment getEnvironment() {
+ if (mEnvironment == null) {
+ mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+ }
+ return mEnvironment;
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ private ForegroundServiceController getFsc() {
+ if (mFsc == null) {
+ mFsc = Dependency.get(ForegroundServiceController.class);
+ }
+ return mFsc;
+ }
+
+ private NotificationLockscreenUserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ }
+ return mUserManager;
+ }
+
+
+ /**
+ * @return true if the provided notification should NOT be shown right now.
+ */
+ public boolean shouldFilterOut(NotificationData.Entry entry) {
+ final StatusBarNotification sbn = entry.notification;
+ if (!(getEnvironment().isDeviceProvisioned()
+ || showNotificationEvenIfUnprovisioned(sbn))) {
+ return true;
+ }
+
+ if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ return true;
+ }
+
+ if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+ && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+ || getUserManager().shouldHideNotifications(sbn.getUserId())
+ || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+ return true;
+ }
+
+ if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+ return true;
+ }
+
+ if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+ return true;
+ }
+
+ if (entry.suspended) {
+ return true;
+ }
+
+ if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
+ return true;
+ }
+
+ if (getFsc().isDungeonNotification(sbn)
+ && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+ // this is a foreground-service disclosure for a user that does not need to show one
+ return true;
+ }
+ if (getFsc().isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Q: What kinds of notifications should show during setup?
+ // A: Almost none! Only things coming from packages with permission
+ // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+ // as relevant for setup (see below).
+ private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+ return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+ }
+
+ @VisibleForTesting
+ static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+ StatusBarNotification sbn) {
+ return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+ sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+ }
+
+ private static int checkUidPermission(IPackageManager packageManager, String permission,
+ int uid) {
+ try {
+ return packageManager.checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 000000000000..8bd0e9ad3c8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+ private static final String TAG = "InterruptionStateProvider";
+ private static final boolean DEBUG = false;
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+ private final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+ private final IDreamManager mDreamManager;
+
+ private NotificationPresenter mPresenter;
+ private ShadeController mShadeController;
+ private HeadsUpManager mHeadsUpManager;
+ private HeadsUpSuppressor mHeadsUpSuppressor;
+
+ private ContentObserver mHeadsUpObserver;
+ @VisibleForTesting
+ protected boolean mUseHeadsUp = false;
+ private boolean mDisableNotificationAlerts;
+
+ public NotificationInterruptionStateProvider(Context context) {
+ this(context,
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+ IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+ }
+
+ @VisibleForTesting
+ protected NotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ mContext = context;
+ mPowerManager = powerManager;
+ mDreamManager = dreamManager;
+ }
+
+ /** Sets up late-binding dependencies for this component. */
+ public void setUpWithPresenter(
+ NotificationPresenter notificationPresenter,
+ HeadsUpManager headsUpManager,
+ HeadsUpSuppressor headsUpSuppressor) {
+ mPresenter = notificationPresenter;
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpSuppressor = headsUpSuppressor;
+
+ mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG,
+ "dismissing any existing heads up notification on disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
+
+ if (ENABLE_HEADS_UP) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ true,
+ mHeadsUpObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+ mHeadsUpObserver);
+ }
+ mHeadsUpObserver.onChange(true); // set up
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+ // message bubble from the bubble to go through heads up path
+ boolean inShade = mStatusBarStateController.getState() == SHADE;
+ if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
+ return false;
+ }
+
+ boolean isDreaming = false;
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ }
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressPeek()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ public boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressAmbient()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ Bundle extras = sbn.getNotification().extras;
+ CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+ CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+ if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link #shouldHeadsUp(NotificationData.Entry)} and
+ * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks
+ * should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationFilter.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
+ return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+ }
+
+ /** Sets whether to disable all alerts. */
+ public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+ mDisableNotificationAlerts = disableNotificationAlerts;
+ mHeadsUpObserver.onChange(true);
+ }
+
+ protected NotificationPresenter getPresenter() {
+ return mPresenter;
+ }
+
+ /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+ public interface HeadsUpSuppressor {
+ /**
+ * Returns false if the provided notification is ineligible for heads-up according to this
+ * component.
+ *
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return false if the notification can not be heads upped
+ */
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
new file mode 100644
index 000000000000..b241b8a70b3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Handles inflating and updating views for notifications. */
+@Singleton
+public class NotificationRowBinder {
+
+ private static final String TAG = "NotificationViewManager";
+
+ private final NotificationGroupManager mGroupManager =
+ Dependency.get(NotificationGroupManager.class);
+ private final NotificationGutsManager mGutsManager =
+ Dependency.get(NotificationGutsManager.class);
+ private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+
+ private final Context mContext;
+ private final IStatusBarService mBarService;
+ private final NotificationMessagingUtil mMessagingUtil;
+ private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
+ this::logNotificationExpansion;
+
+ private NotificationRemoteInputManager mRemoteInputManager;
+ private NotificationPresenter mPresenter;
+ private NotificationListContainer mListContainer;
+ private HeadsUpManager mHeadsUpManager;
+ private NotificationInflater.InflationCallback mInflationCallback;
+ private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
+ private BindRowCallback mBindRowCallback;
+ private NotificationClicker mNotificationClicker;
+
+ @Inject
+ public NotificationRowBinder(Context context) {
+ mContext = context;
+ mMessagingUtil = new NotificationMessagingUtil(context);
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ private NotificationRemoteInputManager getRemoteInputManager() {
+ if (mRemoteInputManager == null) {
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ }
+ return mRemoteInputManager;
+ }
+
+ /**
+ * Sets up late-bound dependencies for this component.
+ */
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationListContainer listContainer,
+ HeadsUpManager headsUpManager,
+ NotificationInflater.InflationCallback inflationCallback,
+ BindRowCallback bindRowCallback) {
+ mPresenter = presenter;
+ mListContainer = listContainer;
+ mHeadsUpManager = headsUpManager;
+ mInflationCallback = inflationCallback;
+ mBindRowCallback = bindRowCallback;
+ mOnAppOpsClickListener = mGutsManager::openGuts;
+ }
+
+ public void setNotificationClicker(NotificationClicker clicker) {
+ mNotificationClicker = clicker;
+ }
+
+ /**
+ * Inflates the views for the given entry (possibly asynchronously).
+ */
+ public void inflateViews(NotificationData.Entry entry, Runnable onDismissRunnable,
+ boolean isUpdate) {
+ ViewGroup parent = mListContainer.getViewParentForNotification(entry);
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ entry.notification.getUser().getIdentifier());
+
+ final StatusBarNotification sbn = entry.notification;
+ if (entry.rowExists()) {
+ entry.reset();
+ updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
+ } else {
+ new RowInflaterTask().inflate(mContext, parent, entry,
+ row -> {
+ bindRow(entry, pmUser, sbn, row, onDismissRunnable);
+ updateNotification(entry, pmUser, sbn, row, isUpdate);
+ });
+ }
+ }
+
+ private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row,
+ Runnable onDismissRunnable) {
+ row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setOnExpandClickListener(mPresenter);
+ row.setInflationCallback(mInflationCallback);
+ row.setLongPressListener(getNotificationLongClicker());
+ mListContainer.bindRow(row);
+ getRemoteInputManager().bindRow(row);
+
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ row.setAppName(appname);
+ row.setOnDismissRunnable(onDismissRunnable);
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ if (ENABLE_REMOTE_INPUT) {
+ row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ }
+
+ row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+
+ mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
+ }
+
+ /**
+ * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
+ * reinflating them.
+ */
+ public void onNotificationRankingUpdated(
+ NotificationData.Entry entry,
+ @Nullable Integer oldImportance,
+ NotificationUiAdjustment oldAdjustment,
+ NotificationUiAdjustment newAdjustment,
+ boolean isUpdate) {
+ if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
+ if (entry.rowExists()) {
+ entry.reset();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ mContext,
+ entry.notification.getUser().getIdentifier());
+ updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate);
+ } else {
+ // Once the RowInflaterTask is done, it will pick up the updated entry, so
+ // no-op here.
+ }
+ } else {
+ if (oldImportance != null && entry.importance != oldImportance) {
+ if (entry.rowExists()) {
+ entry.getRow().onNotificationRankingUpdated();
+ }
+ }
+ }
+ }
+
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
+ private void updateNotification(
+ NotificationData.Entry entry,
+ PackageManager pmUser,
+ StatusBarNotification sbn,
+ ExpandableNotificationRow row,
+ boolean isUpdate) {
+ boolean isLowPriority = entry.ambient;
+ boolean wasLowPriority = row.isLowPriority();
+ row.setIsLowPriority(isLowPriority);
+ row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+ // bind the click event to the content area
+ checkNotNull(mNotificationClicker).register(row, sbn);
+
+ // Extract target SDK version.
+ try {
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
+ entry.targetSdk = info.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException ex) {
+ Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+ }
+ row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+ && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+
+ // TODO: should updates to the entry be happening somewhere else?
+ entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
+
+ entry.setRow(row);
+ row.setOnActivatedListener(mPresenter);
+
+ boolean useIncreasedCollapsedHeight =
+ mMessagingUtil.isImportantMessaging(sbn, entry.importance);
+ boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+ && !mPresenter.isPresenterFullyCollapsed();
+ row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+ row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ row.setEntry(entry);
+
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
+ }
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
+ }
+ row.setNeedsRedaction(
+ Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
+ row.inflateViews();
+ }
+
+ ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ return mGutsManager::openGuts;
+ }
+
+ private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+ mUiOffloadThread.submit(() -> {
+ try {
+ mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ });
+ }
+
+ /** Callback for when a row is bound to an entry. */
+ public interface BindRowCallback {
+ /**
+ * Called when a new notification and row is created.
+ *
+ * @param entry entry for the notification
+ * @param pmUser package manager for user
+ * @param sbn notification
+ * @param row row for the notification
+ */
+ void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fce79800193d..abb7b4161984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -25,10 +25,14 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A manager that ensures that notifications are visually stable. It will suppress reorderings
* and reorder at the right time when they are out of view.
*/
+@Singleton
public class VisualStabilityManager implements OnHeadsUpChangedListener {
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
@@ -42,6 +46,10 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener {
private ArraySet<View> mAddedChildren = new ArraySet<>();
private boolean mPulsing;
+ @Inject
+ public VisualStabilityManager() {
+ }
+
/**
* Add a callback to invoke when reordering is allowed again.
* @param callback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9f02e543b6e3..eb1fc30843b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -40,10 +40,14 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
+@Singleton
public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger";
@@ -145,6 +149,7 @@ public class NotificationLogger implements StateListener {
}
};
+ @Inject
public NotificationLogger() {
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1d79152bb1cc..8b0a6828205d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -800,7 +800,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
@Override
- public void performRemoveAnimation(long duration, long delay,
+ public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
@@ -812,6 +812,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
} else if (onFinishedRunnable != null) {
onFinishedRunnable.run();
}
+ return 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 91d08fffa642..a58c7cde32a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2612,6 +2612,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
+ if (mMenuRow.isMenuVisible()) {
+ Animator anim = getTranslateViewAnimator(0f, null /* listener */);
+ if (anim != null) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ExpandableNotificationRow.super.performRemoveAnimation(
+ duration, delay, translationDirection, isHeadsUpAnimation,
+ endLocation, onFinishedRunnable, animationListener);
+ }
+ });
+ anim.start();
+ return anim.getDuration();
+ }
+ }
+ return super.performRemoveAnimation(duration, delay, translationDirection,
+ isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+ }
+
+ @Override
protected void onAppearAnimationFinished(boolean wasAppearing) {
super.onAppearAnimationFinished(wasAppearing);
if (wasAppearing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index b1fa6a531438..d1a89b4e493f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -312,16 +312,19 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
* @param duration The duration of the remove animation.
* @param delay The delay of the animation
* @param translationDirection The direction value from [-1 ... 1] indicating in which the
- * animation should be performed. A value of -1 means that The
- * remove animation should be performed upwards,
- * such that the child appears to be going away to the top. 1
- * Should mean the opposite.
+ * animation should be performed. A value of -1 means that The
+ * remove animation should be performed upwards,
+ * such that the child appears to be going away to the top. 1
+ * Should mean the opposite.
* @param isHeadsUpAnimation Is this a headsUp animation.
* @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
+ *
+ * @return The additional delay, in milliseconds, that this view needs to add before the
+ * animation starts.
*/
- public abstract void performRemoveAnimation(long duration,
+ public abstract long performRemoveAnimation(long duration,
long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 16796dd86e18..607d96dcbd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -34,10 +34,14 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manager for the notification blocking helper - tracks and helps create the blocking helper
* affordance.
*/
+@Singleton
public class NotificationBlockingHelperManager {
/** Enables debug logging and always makes the blocking helper show up after a dismiss. */
private static final boolean DEBUG = false;
@@ -54,6 +58,7 @@ public class NotificationBlockingHelperManager {
*/
private boolean mIsShadeExpanded;
+ @Inject
public NotificationBlockingHelperManager(Context context) {
mContext = context;
mNonBlockablePkgs = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 6bc39c82f8bf..02a310c29379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1474,9 +1474,19 @@ public class NotificationContentView extends FrameLayout {
if (mExpandedChild != null) {
mExpandedSmartReplyView =
applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
- if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
- mSmartReplyController.smartRepliesAdded(
- entry, smartRepliesAndActions.smartReplies.choices.length);
+ if (mExpandedSmartReplyView != null) {
+ if (smartRepliesAndActions.smartReplies != null
+ || smartRepliesAndActions.smartActions != null) {
+ int numSmartReplies = smartRepliesAndActions.smartReplies == null
+ ? 0 : smartRepliesAndActions.smartReplies.choices.length;
+ int numSmartActions = smartRepliesAndActions.smartActions == null
+ ? 0 : smartRepliesAndActions.smartActions.actions.size();
+ boolean fromAssistant = smartRepliesAndActions.smartReplies == null
+ ? smartRepliesAndActions.smartActions.fromAssistant
+ : smartRepliesAndActions.smartReplies.fromAssistant;
+ mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
+ numSmartActions, fromAssistant);
+ }
}
}
if (mHeadsUpChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2e4552701456..ac4e5830d76b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -57,10 +57,14 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
+@Singleton
public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
private static final String TAG = "NotificationGutsManager";
@@ -91,6 +95,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
+ @Inject
public NotificationGutsManager(Context context) {
mContext = context;
mAccessibilityManager = (AccessibilityManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 1b40c06a5867..eaa2eaf21927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -190,12 +190,13 @@ public abstract class StackScrollerDecorView extends ExpandableView {
}
@Override
- public void performRemoveAnimation(long duration, long delay,
+ public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
setContentVisible(false);
+ return 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dbe6e8ec764d..67a5cd934a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,10 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
- .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
- .ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
import android.animation.Animator;
@@ -651,6 +649,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
< mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
|| mAmbientState.isDark())) {
drawBackground(canvas);
+ } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+ drawHeadsUpBackground(canvas);
}
if (DEBUG) {
@@ -749,6 +749,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mCornerRadius, mCornerRadius, mBackgroundPaint);
}
+ private void drawHeadsUpBackground(Canvas canvas) {
+ int left = mSidePaddings;
+ int right = getWidth() - mSidePaddings;
+
+ float top = getHeight();
+ float bottom = 0;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+ top = Math.min(top, row.getTranslationY());
+ bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+ }
+ }
+ }
+
+ if (top < bottom) {
+ canvas.drawRoundRect(
+ left, top, right, bottom,
+ mCornerRadius, mCornerRadius, mBackgroundPaint);
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateBackgroundDimming() {
// No need to update the background color if it's not being drawn.
@@ -2157,19 +2183,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
return;
}
- NotificationSection firstSection = getFirstVisibleSection();
- int top = 0;
- if (firstSection != null) {
- ActivatableNotificationView firstView = firstSection.getFirstVisibleChild();
- // Round Y up to avoid seeing the background during animation
- int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
- if (mAnimateNextBackgroundTop || firstSection.isTargetTop(finalTranslationY)) {
- // we're ending up at the same location as we are now, lets just skip the animation
- top = finalTranslationY;
- } else {
- top = (int) Math.ceil(firstView.getTranslationY());
- }
- }
+ int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
NotificationSection lastSection = getLastVisibleSection();
ActivatableNotificationView lastView =
mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
@@ -2177,21 +2191,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
: lastSection == null ? null : lastSection.getLastVisibleChild();
int bottom;
if (lastView != null) {
- int finalTranslationY;
- if (lastView == mShelf) {
- finalTranslationY = (int) mShelf.getTranslationY();
- } else {
- finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
- }
- int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
- int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
- if (mAnimateNextBackgroundBottom || lastSection.isTargetBottom(finalBottom)) {
- // we're ending up at the same location as we are now, lets just skip the animation
- bottom = finalBottom;
- } else {
- bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
- - lastView.getClipBottomAmount());
- }
+ bottom = getSectionBottomOrFinalBottom(
+ lastSection, lastView, mAnimateNextBackgroundBottom);
} else {
top = mTopPadding;
bottom = top;
@@ -2207,6 +2208,57 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
}
+ private int getSectionTopOrFinalTop(
+ @Nullable NotificationSection section, boolean alreadyAnimating) {
+ int top = 0;
+ if (section != null) {
+ ActivatableNotificationView firstView = section.getFirstVisibleChild();
+ // Round Y up to avoid seeing the background during animation
+ int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+ if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+ // we're ending up at the same location as we are now, let's just skip the animation
+ top = finalTranslationY;
+ } else {
+ top = (int) Math.ceil(firstView.getTranslationY());
+ }
+ }
+ return top;
+ }
+
+ private int getSectionBottomOrFinalBottom(
+ @Nullable NotificationSection section, boolean alreadyAnimating) {
+ return section == null ? 0
+ : getSectionBottomOrFinalBottom(
+ section, section.getLastVisibleChild(), alreadyAnimating);
+ }
+
+ private int getSectionBottomOrFinalBottom(
+ NotificationSection section,
+ ActivatableNotificationView lastView,
+ boolean alreadyAnimating) {
+ int bottom = 0;
+ if (lastView != null) {
+ float finalTranslationY;
+ if (lastView == mShelf) {
+ finalTranslationY = mShelf.getTranslationY();
+ } else {
+ finalTranslationY = ViewState.getFinalTranslationY(lastView);
+ }
+ int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+ // Round Y down to avoid seeing the background during animation
+ int finalBottom = (int) Math.floor(
+ finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+ if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
+ // we're ending up at the same location as we are now, lets just skip the animation
+ bottom = finalBottom;
+ } else {
+ bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+ - lastView.getClipBottomAmount());
+ }
+ }
+ return bottom;
+ }
+
private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -2214,13 +2266,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
if (lastChildAboveGap != null && firstChildBelowGap != null) {
- int gapTop =
- (int) Math.max(top,
- Math.min(lastChildAboveGap.getTranslationY()
- + lastChildAboveGap.getActualHeight(),
- bottom));
- int gapBottom = (int) Math.max(top,
- Math.min(firstChildBelowGap.getTranslationY(), bottom));
+ int gapTop = getSectionBottomOrFinalBottom(
+ highPrioritySection, mAnimateNextSectionBoundsChange);
+ gapTop = Math.max(top, Math.min(gapTop, bottom));
+
+ int gapBottom = getSectionTopOrFinalTop(
+ lowPrioritySection, mAnimateNextSectionBoundsChange);
+ gapBottom = Math.max(top, Math.min(gapBottom, bottom));
+
highPrioritySection.getBounds().set(left, top, right, gapTop);
lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
} else if (lastChildAboveGap != null) {
@@ -5574,15 +5627,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (translatingParentView != null && row == translatingParentView) {
mSwipeHelper.clearExposedMenuView();
mSwipeHelper.clearTranslatingParentView();
+ if (row instanceof ExpandableNotificationRow) {
+ mHeadsUpManager.setMenuShown(
+ ((ExpandableNotificationRow) row).getEntry(), false);
+
+ }
}
}
@Override
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
- ((ExpandableNotificationRow) row).getStatusBarNotification()
- .getPackageName());
+ notificationRow.getStatusBarNotification().getPackageName());
+ mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
}
mSwipeHelper.onMenuShown(row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d6905478a043..19fce485b0c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -406,13 +406,8 @@ public class StackStateAnimator {
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
- 0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, new Runnable() {
- @Override
- public void run() {
- removeTransientView(changingView);
- }
- }, null);
+ 0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+ 0, () -> removeTransientView(changingView), null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
@@ -507,10 +502,11 @@ public class StackStateAnimator {
// We need to add the global animation listener, since once no animations are
// running anymore, the panel will instantly hide itself. We need to wait until
// the animation is fully finished for this though.
- changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
- + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
- true /* isHeadsUpAppear */, targetLocation, endRunnable,
- getGlobalAnimationFinishedListener());
+ long removeAnimationDelay = changingView.performRemoveAnimation(
+ ANIMATION_DURATION_HEADS_UP_DISAPPEAR + ANIMATION_DELAY_HEADS_UP,
+ extraDelay, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+ endRunnable, getGlobalAnimationFinishedListener());
+ mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
endRunnable.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aa0b7b656e1c..f4cfd4197637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -264,6 +264,17 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
}
+ /**
+ * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
+ * area if it's pinned until it's hidden again.
+ */
+ public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+ if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
+ ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpManager public methods overrides:
@@ -469,6 +480,14 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
// HeadsUpEntryPhone:
protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+
+ private boolean mMenuShownPinned;
+
+ @Override
+ protected boolean isSticky() {
+ return super.isSticky() || mMenuShownPinned;
+ }
+
public void setEntry(@NonNull final NotificationData.Entry entry) {
Runnable removeHeadsUpRunnable = () -> {
if (!mVisualStabilityManager.isReorderingAllowed()) {
@@ -510,6 +529,25 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
updateEntry(false /* updatePostTime */);
}
}
+
+ public void setMenuShownPinned(boolean menuShownPinned) {
+ if (mMenuShownPinned == menuShownPinned) {
+ return;
+ }
+
+ mMenuShownPinned = menuShownPinned;
+ if (menuShownPinned) {
+ removeAutoRemovalCallbacks();
+ } else {
+ updateEntry(false /* updatePostTime */);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ mMenuShownPinned = false;
+ }
}
public interface AnimationStateHandler {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index b3d0bf8abf62..e541e14b3d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -20,15 +20,23 @@ import android.util.Log;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Executes actions that require the screen to be unlocked. Delegates the actual handling to an
* implementation passed via {@link #setDismissHandler}.
*/
+@Singleton
public class KeyguardDismissUtil implements KeyguardDismissHandler {
private static final String TAG = "KeyguardDismissUtil";
private volatile KeyguardDismissHandler mDismissHandler;
+ @Inject
+ public KeyguardDismissUtil() {
+ }
+
/** Sets the actual {@link DismissHandler} implementation. */
public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
mDismissHandler = dismissHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 96b753679796..5ba59b507fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -466,6 +466,14 @@ public class KeyguardStatusBarView extends RelativeLayout
.onDensityOrFontScaleChanged();
}
+ @Override
+ public void onOverlayChanged() {
+ mCarrierLabel.setTextAppearance(
+ Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
+ onThemeChanged();
+ mBatteryView.updatePercentView();
+ }
+
private void updateIconsAndTextColors() {
@ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 67b077e729df..7c30e48f171a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -76,11 +76,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private final Rect mLastDockedBounds = new Rect();
private boolean mQsCustomizing;
+ private final Context mContext;
+
public LightBarController(Context ctx) {
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
mStatusBarIconController = Dependency.get(DarkIconDispatcher.class);
mBatteryController = Dependency.get(BatteryController.class);
mBatteryController.addCallback(this);
+ mContext = ctx;
}
public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -217,8 +220,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private void updateNavigation() {
if (mNavigationBarController != null) {
- mNavigationBarController.setIconsDark(
- mNavigationLight, animateChange());
+ if (!NavBarTintController.isEnabled(mContext)) {
+ mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 57cc7d6c1ecb..7876aa5d89d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.MathUtils;
+import android.provider.Settings;
import android.util.TimeUtils;
import com.android.systemui.Dependency;
@@ -42,13 +46,14 @@ import java.io.PrintWriter;
public class LightBarTransitionsController implements Dumpable, Callbacks,
StatusBarStateController.StateListener {
- public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
+ public static final int DEFAULT_TINT_ANIMATION_DURATION = 120;
private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
private final Handler mHandler;
private final DarkIntensityApplier mApplier;
private final KeyguardMonitor mKeyguardMonitor;
private final StatusBarStateController mStatusBarStateController;
+ private NavBarTintController mColorAdaptionController;
private boolean mTransitionDeferring;
private long mTransitionDeferringStartTime;
@@ -67,6 +72,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
}
};
+ private final Context mContext;
+
public LightBarTransitionsController(Context context, DarkIntensityApplier applier) {
mApplier = applier;
mHandler = new Handler();
@@ -76,6 +83,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
.addCallback(this);
mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mContext = context;
}
public void destroy(Context context) {
@@ -106,7 +114,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
public void appTransitionCancelled() {
if (mTransitionPending && mTintChangePending) {
mTintChangePending = false;
- animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+ animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
}
mTransitionPending = false;
}
@@ -146,8 +154,17 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
mTransitionDeferringDuration);
} else {
- animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+ animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration());
+ }
+ }
+
+ public long getTintAnimationDuration() {
+ if (NavBarTintController.isEnabled(mContext)) {
+ return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
+ NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION),
+ MIN_COLOR_ADAPT_TRANSITION_TIME);
}
+ return DEFAULT_TINT_ANIMATION_DURATION;
}
public float getCurrentDarkIntensity() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
new file mode 100644
index 000000000000..9ecee1825f07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+
+public class NavBarTintController {
+ public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition";
+ public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
+
+ private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
+ private Handler mColorAdaptionHandler;
+
+ // Poll time for each iteration to color sample
+ private static final int COLOR_ADAPTION_TIMEOUT = 300;
+
+ // Passing the threshold of this luminance value will make the button black otherwise white
+ private static final float LUMINANCE_THRESHOLD = 0.3f;
+
+ // The home button's icon is actually smaller than the button's size, the percentage will
+ // cut into the button's size to determine the icon size
+ private static final float PERCENTAGE_BUTTON_PADDING = 0.3f;
+
+ // The distance from the home button to color sample around
+ private static final int COLOR_SAMPLE_MARGIN = 20;
+
+ private boolean mRunning;
+
+ private final NavigationBarView mNavigationBarView;
+ private final LightBarTransitionsController mLightBarController;
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ public NavBarTintController(NavigationBarView navigationBarView,
+ LightBarTransitionsController lightBarController) {
+ mNavigationBarView = navigationBarView;
+ mLightBarController = lightBarController;
+ }
+
+ public void start() {
+ if (!isEnabled(mNavigationBarView.getContext())) {
+ return;
+ }
+ if (mColorAdaptionHandler == null) {
+ mColorAdaptHandlerThread.start();
+ mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper());
+ }
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ mColorAdaptionHandler.post(this::updateTint);
+ mRunning = true;
+ }
+
+ public void end() {
+ if (mColorAdaptionHandler != null) {
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ }
+ mRunning = false;
+ }
+
+ public void stop() {
+ end();
+ if (mColorAdaptionHandler != null) {
+ mColorAdaptHandlerThread.quitSafely();
+ }
+ }
+
+ private void updateTint() {
+ int[] navPos = new int[2];
+ int[] butPos = new int[2];
+ if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+ return;
+ }
+
+ // Determine the area of the home icon in the larger home button
+ mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos);
+ final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth();
+ final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight();
+ final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth);
+ final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight);
+ final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding,
+ navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding);
+ if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) {
+ scheduleColorAdaption();
+ return;
+ }
+ mNavigationBarView.getCurrentView().getLocationOnScreen(navPos);
+ homeButtonRect.offset(navPos[0], navPos[1]);
+
+ // Apply a margin area around the button region to sample the colors, crop from screenshot
+ final Rect cropRect = new Rect(homeButtonRect);
+ cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN);
+ if (cropRect.isEmpty()) {
+ scheduleColorAdaption();
+ return;
+ }
+
+ // Determine the size of the home area
+ Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN,
+ homeButtonRect.width() + COLOR_SAMPLE_MARGIN,
+ homeButtonRect.height() + COLOR_SAMPLE_MARGIN);
+
+ // Get the screenshot around the home button icon to determine the color
+ DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics);
+ final Bitmap hardBitmap = SurfaceControl
+ .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ mNavigationBarView.getContext().getDisplay().getRotation());
+ if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) {
+ final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
+ cropRect.width(), cropRect.height());
+ final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false);
+
+ // Get the luminance value to determine if the home button should be black or white
+ final int[] pixels = new int[softBitmap.getByteCount() / 4];
+ softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(),
+ softBitmap.getHeight());
+ float r = 0, g = 0, blue = 0;
+
+ int width = cropRect.width();
+ int total = 0;
+ for (int i = 0; i < pixels.length; i += 4) {
+ int x = i % width;
+ int y = i / width;
+ if (!homeArea.contains(x, y)) {
+ r += Color.red(pixels[i]);
+ g += Color.green(pixels[i]);
+ blue += Color.blue(pixels[i]);
+ total++;
+ }
+ }
+
+ r /= total;
+ g /= total;
+ blue /= total;
+
+ r = Math.max(Math.min(r / 255f, 1), 0);
+ g = Math.max(Math.min(g / 255f, 1), 0);
+ blue = Math.max(Math.min(blue / 255f, 1), 0);
+
+ if (r <= 0.03928) {
+ r /= 12.92;
+ } else {
+ r = (float) Math.pow((r + 0.055) / 1.055, 2.4);
+ }
+ if (g <= 0.03928) {
+ g /= 12.92;
+ } else {
+ g = (float) Math.pow((g + 0.055) / 1.055, 2.4);
+ }
+ if (blue <= 0.03928) {
+ blue /= 12.92;
+ } else {
+ blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4);
+ }
+
+ if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) {
+ // Black
+ mMainHandler.post(
+ () -> mLightBarController
+ .setIconsDark(true /* dark */, true /* animate */));
+ } else {
+ // White
+ mMainHandler.post(
+ () -> mLightBarController
+ .setIconsDark(false /* dark */, true /* animate */));
+ }
+ cropBitmap.recycle();
+ hardBitmap.recycle();
+ }
+ scheduleColorAdaption();
+ }
+
+ private void scheduleColorAdaption() {
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ if (!mRunning || !isEnabled(mNavigationBarView.getContext())) {
+ return;
+ }
+ mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT);
+ }
+
+ public static boolean isEnabled(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index ae0a1452905d..2daff2cb18bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -72,7 +72,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
@@ -97,6 +96,8 @@ import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Fragment containing the NavigationBarFragment. Contains logic for what happens
* on clicks and view states of the nav bar.
@@ -111,11 +112,12 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
- private final DeviceProvisionedController mDeviceProvisionedController =
- Dependency.get(DeviceProvisionedController.class);
+ private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ protected final AssistManager mAssistManager;
+ private final MetricsLogger mMetricsLogger;
+ private final DeviceProvisionedController mDeviceProvisionedController;
protected NavigationBarView mNavigationBarView = null;
- protected AssistManager mAssistManager;
private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -124,7 +126,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
private AccessibilityManager mAccessibilityManager;
private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -193,6 +194,17 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
}
};
+ @Inject
+ public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
+ DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
+ AssistManager assistManager, OverviewProxyService overviewProxyService) {
+ mAccessibilityManagerWrapper = accessibilityManagerWrapper;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mMetricsLogger = metricsLogger;
+ mAssistManager = assistManager;
+ mOverviewProxyService = overviewProxyService;
+ }
+
// ----- Fragment Lifecycle Callbacks -----
@Override
@@ -205,8 +217,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
mWindowManager = getContext().getSystemService(WindowManager.class);
mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
- Dependency.get(AccessibilityManagerWrapper.class).addCallback(
- mAccessibilityListener);
mContentResolver = getContext().getContentResolver();
mMagnificationObserver = new MagnificationContentObserver(
getContext().getMainThreadHandler());
@@ -218,15 +228,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
}
- mAssistManager = Dependency.get(AssistManager.class);
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
}
@Override
public void onDestroy() {
super.onDestroy();
- Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
- mAccessibilityListener);
+ mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
}
@@ -851,6 +859,16 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn();
+
+ if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ // Enabled and screen is on, start it again if enabled
+ if (NavBarTintController.isEnabled(getContext())) {
+ mNavigationBarView.getColorAdaptionController().start();
+ }
+ } else {
+ // Screen off disable it
+ mNavigationBarView.getColorAdaptionController().end();
+ }
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
@@ -882,7 +900,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = new NavigationBarFragment();
+ final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
+ .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 30e840926698..6a7983af862d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -149,6 +149,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private NavBarTintController mColorAdaptionController;
private NavigationPrototypeController mPrototypeController;
private NavigationGestureAction[] mDefaultGestureMap;
private QuickScrubAction mQuickScrubAction;
@@ -277,6 +278,15 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
public void onBackButtonVisibilityChanged(boolean visible) {
getBackButton().setVisibility(visible ? VISIBLE : GONE);
}
+
+ @Override
+ public void onColorAdaptChanged(boolean enabled) {
+ if (enabled) {
+ mColorAdaptionController.start();
+ } else {
+ mColorAdaptionController.end();
+ }
+ }
};
public NavigationBarView(Context context, AttributeSet attrs) {
@@ -334,6 +344,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
mPrototypeController.register();
mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
+ mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+ }
+
+ public NavBarTintController getColorAdaptionController() {
+ return mColorAdaptionController;
}
public BarTransitions getBarTransitions() {
@@ -1097,6 +1112,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+ mColorAdaptionController.start();
}
@Override
@@ -1107,6 +1123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
mGestureHelper.destroy();
}
mPrototypeController.unregister();
+ mColorAdaptionController.stop();
setUpSwipeUpOnboarding(false);
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index b11b6d472713..40ac79376b06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,6 +37,7 @@ public class NavigationPrototypeController extends ContentObserver {
static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+ public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
@Retention(RetentionPolicy.SOURCE)
@IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
@@ -73,6 +74,7 @@ public class NavigationPrototypeController extends ContentObserver {
public void register() {
registerObserver(HIDE_BACK_BUTTON_SETTING);
registerObserver(GESTURE_MATCH_SETTING);
+ registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
}
/**
@@ -96,6 +98,9 @@ public class NavigationPrototypeController extends ContentObserver {
} else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
mListener.onBackButtonVisibilityChanged(
!getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+ } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
+ mListener.onColorAdaptChanged(
+ NavBarTintController.isEnabled(mContext));
}
} catch (SettingNotFoundException e) {
e.printStackTrace();
@@ -138,5 +143,6 @@ public class NavigationPrototypeController extends ContentObserver {
public interface OnPrototypeChangedListener {
void onGestureRemap(@GestureAction int[] actions);
void onBackButtonVisibilityChanged(boolean visible);
+ void onColorAdaptChanged(boolean enabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index dd81c4e20d6d..b1f74c8ab91e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -29,8 +29,8 @@ import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListen
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -42,11 +42,15 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManager},
* {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
* the correct notification in a group alerting based off the group suppression.
*/
+@Singleton
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
OnAmbientChangedListener, StateListener {
@@ -73,6 +77,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
private boolean mIsDozing;
+ @Inject
public NotificationGroupAlertTransferHelper() {
Dependency.get(StatusBarStateController.class).addCallback(this);
}
@@ -90,7 +95,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// not being up to date.
mEntryManager = entryManager;
- mEntryManager.setAlertTransferListener(mAlertTransferListener);
+ mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
}
@@ -181,7 +186,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
}
}
- private final AlertTransferListener mAlertTransferListener = new AlertTransferListener() {
+ private final NotificationEntryListener mNotificationEntryListener =
+ new NotificationEntryListener() {
// Called when a new notification has been posted but is not inflated yet. We use this to
// see as early as we can if we need to abort a transfer.
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8f4369a98b17..3c1c0765a3b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -39,9 +39,13 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A class to handle notifications and their corresponding groups.
*/
+@Singleton
public class NotificationGroupManager implements OnHeadsUpChangedListener,
OnAmbientChangedListener, StateListener {
@@ -54,6 +58,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private boolean mIsUpdatingUnchangedGroup;
+ @Inject
public NotificationGroupManager() {
Dependency.get(StatusBarStateController.class).addCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d56274640..e40835fb8fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -207,7 +207,7 @@ public class NotificationIconAreaController implements DarkReceiver {
}
// showAmbient == show in shade but not shelf
- if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+ if (!showAmbient && entry.shouldSuppressStatusBar()) {
return false;
}
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 6caea1d7a8c7..008c21d1b936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,9 +187,12 @@ import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -375,6 +378,8 @@ public class StatusBar extends SystemUI implements DemoMode,
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private NotificationRowBinder mNotificationRowBinder;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
protected AppOpsController mAppOpsController;
@@ -619,6 +624,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
mAppOpsController = Dependency.get(AppOpsController.class);
@@ -630,8 +638,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
- mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
-
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
@@ -1015,10 +1021,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
protected QS createDefaultQSFragment() {
- return new QSFragment();
+ return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class);
}
- protected void setUpPresenter() {
+ private void setUpPresenter() {
// Set up the initial notification state.
mActivityLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindow, this, mNotificationPanel,
@@ -1036,7 +1042,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
+ this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
+
+ mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
}
/**
@@ -1162,6 +1171,10 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onOverlayChanged();
}
+ // We need the new R.id.keyguard_indication_area before recreating
+ // mKeyguardIndicationController
+ mNotificationPanel.onThemeChanged();
+ onThemeChanged();
}
@Override
@@ -1404,7 +1417,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mEntryManager.setDisableNotificationAlerts(
+ mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
(state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
}
@@ -2230,6 +2243,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mNavigationBar.getBarTransitions().setAutoDim(false);
}
mHandler.removeCallbacks(mAutoDim);
+
+ // Do not dim the navigation buttons if the its tint is controlled by the bar's background
+ if (NavBarTintController.isEnabled(mContext)) {
+ return;
+ }
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index c93d151b0f52..8d1b911b101f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -24,6 +24,7 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -33,15 +34,21 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
@@ -54,7 +61,9 @@ import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
+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.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@ import com.android.systemui.statusbar.policy.PreviewInflater;
public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
private static final String TAG = "NotificationClickHandler";
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
Dependency.get(NotificationEntryManager.class);
private final StatusBarStateController mStatusBarStateController =
Dependency.get(StatusBarStateController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Context mContext;
private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
+ private final IDreamManager mDreamManager;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -112,6 +126,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mCommandQueue = getComponent(context, CommandQueue.class);
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationData.Entry entry) {
+ handleFullScreenIntent(entry);
+ }
+ });
}
/**
@@ -322,6 +345,45 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}, null, false /* afterKeyguardGone */);
}
+ private void handleFullScreenIntent(NotificationData.Entry entry) {
+ boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+ if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
+ if (shouldSuppressFullScreenIntent(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+ }
+ } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+ }
+ } else {
+ // Stop screensaver if the notification has a fullscreen intent.
+ // (like an incoming phone call)
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
+
+ // not immersive & a fullscreen alert should be shown
+ if (DEBUG) {
+ Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+ }
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+ entry.key);
+ entry.notification.getNotification().fullScreenIntent.send();
+ entry.notifyFullScreenIntentLaunched();
+ mMetricsLogger.count("note_fullscreen", 1);
+ } catch (PendingIntent.CanceledException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
@Override
public boolean isCollapsingToShowActivityOverLockscreen() {
return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|| !mActivityLaunchAnimator.isAnimationPending();
}
+ private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+ if (mPresenter.isDeviceInVrMode()) {
+ return true;
+ }
+
+ return entry.shouldSuppressFullScreenIntent();
+ }
+
private void removeNotification(StatusBarNotification notification) {
// We have to post it to the UI thread for synchronization
Dependency.get(MAIN_HANDLER).post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 261f117b58b4..d643f07e2859 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,10 @@ import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+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.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -88,6 +91,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
Dependency.get(StatusBarStateController.class);
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
+ private final NotificationRowBinder mNotificationRowBinder =
+ Dependency.get(NotificationRowBinder.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
@@ -166,8 +173,37 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
Dependency.get(InitController.class).addPostInitTask(() -> {
+ NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
+ @Override
+ public void onNotificationAdded(Entry entry) {
+ // Recalculate the position of the sliding windows and the titles.
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationUpdated(StatusBarNotification notification) {
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationRemoved(String key, StatusBarNotification old) {
+ StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
+ }
+
+ @Override
+ public void onPerformRemoveNotification(
+ StatusBarNotification statusBarNotification) {
+ StatusBarNotificationPresenter.this.onPerformRemoveNotification();
+ }
+ };
+
mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
- mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(
+ this, notifListContainer, notificationEntryListener, mHeadsUpManager);
+ mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
+ mEntryManager, this);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(
+ this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
@@ -217,10 +253,9 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|| mActivityLaunchAnimator.isAnimationRunning();
}
- @Override
- public void onPerformRemoveNotification(StatusBarNotification n) {
+ private void onPerformRemoveNotification() {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mAmbientPulseManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -244,18 +279,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mNotificationPanel.updateNotificationViews();
}
- @Override
- public void onNotificationAdded(Entry shadeEntry) {
- // Recalculate the position of the sliding windows and the titles.
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
- public void onNotificationUpdated(StatusBarNotification notification) {
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
public void onNotificationRemoved(String key, StatusBarNotification old) {
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -277,7 +300,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- @Override
public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
if (mShadeController.isDozing()) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e72806438f54..a02c9d51fecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,8 +16,7 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,7 +31,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index c2933e1516be..2a10db620096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -27,9 +27,13 @@ import com.android.systemui.qs.QSFragment;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBar;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Let {@link RemoteInputView} to control the visibility of QuickSetting.
*/
+@Singleton
public class RemoteInputQuickSettingsDisabler
implements ConfigurationController.ConfigurationListener {
@@ -39,6 +43,7 @@ public class RemoteInputQuickSettingsDisabler
private int mLastOrientation;
@VisibleForTesting CommandQueue mCommandQueue;
+ @Inject
public RemoteInputQuickSettingsDisabler(Context context) {
mContext = context;
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 71d6e5421b75..6193159ec0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -27,6 +29,11 @@ import android.util.Log;
import com.android.systemui.R;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
public final class SmartReplyConstants extends ContentObserver {
private static final String TAG = "SmartReplyConstants";
@@ -47,7 +54,8 @@ public final class SmartReplyConstants extends ContentObserver {
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
- public SmartReplyConstants(Handler handler, Context context) {
+ @Inject
+ public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
super(handler);
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index dae1472c42af..0a29e04ce20f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -184,7 +184,11 @@ public class PluginFragment extends PreferenceFragment {
mInfo.services[i].name);
if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
- mPluginEnabler.setEnabled(componentName, isEnabled);
+ if (isEnabled) {
+ mPluginEnabler.setEnabled(componentName);
+ } else {
+ mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY);
+ }
shouldSendBroadcast = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
new file mode 100644
index 000000000000..e458e6376580
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QuickStatusBarHeader;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Manages inflation that requires dagger injection.
+ * See docs/dagger.md for details.
+ */
+@Singleton
+public class InjectionInflationController {
+
+ public static final String VIEW_CONTEXT = "view_context";
+ private final ViewCreator mViewCreator;
+ private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+ private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
+
+ @Inject
+ public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
+ mViewCreator = rootComponent.createViewCreator();
+ initInjectionMap();
+ }
+
+ ArrayMap<String, Method> getInjectionMap() {
+ return mInjectionMap;
+ }
+
+ ViewCreator getFragmentCreator() {
+ return mViewCreator;
+ }
+
+ /**
+ * Wraps a {@link LayoutInflater} to support creating dagger injected views.
+ * See docs/dagger.md for details.
+ */
+ public LayoutInflater injectable(LayoutInflater inflater) {
+ LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
+ ret.setPrivateFactory(mFactory);
+ return ret;
+ }
+
+ private void initInjectionMap() {
+ for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
+ if (View.class.isAssignableFrom(method.getReturnType())
+ && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+ mInjectionMap.put(method.getReturnType().getName(), method);
+ }
+ }
+ }
+
+ /**
+ * The subcomponent of dagger that holds all views that need injection.
+ */
+ @Subcomponent
+ public interface ViewCreator {
+ /**
+ * Creates another subcomponent to actually generate the view.
+ */
+ ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
+ }
+
+ /**
+ * Secondary sub-component that actually creates the views.
+ *
+ * Having two subcomponents lets us hide the complexity of providing the named context
+ * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
+ * creates a new ViewInstanceCreator any time we need to inflate a view.
+ */
+ @Subcomponent(modules = ViewAttributeProvider.class)
+ public interface ViewInstanceCreator {
+ /**
+ * Creates the QuickStatusBarHeader.
+ */
+ QuickStatusBarHeader createQsHeader();
+ }
+
+ /**
+ * Module for providing view-specific constructor objects.
+ */
+ @Module
+ public class ViewAttributeProvider {
+ private final Context mContext;
+ private final AttributeSet mAttrs;
+
+ private ViewAttributeProvider(Context context, AttributeSet attrs) {
+ mContext = context;
+ mAttrs = attrs;
+ }
+
+ /**
+ * Provides the view-themed context (as opposed to the global sysui application context).
+ */
+ @Provides
+ @Named(VIEW_CONTEXT)
+ public Context provideContext() {
+ return mContext;
+ }
+
+ /**
+ * Provides the AttributeSet for the current view being inflated.
+ */
+ @Provides
+ public AttributeSet provideAttributeSet() {
+ return mAttrs;
+ }
+ }
+
+ private class InjectionFactory implements LayoutInflater.Factory2 {
+
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ Method creationMethod = mInjectionMap.get(name);
+ if (creationMethod != null) {
+ ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
+ try {
+ return (View) creationMethod.invoke(
+ mViewCreator.createInstanceCreator(provider));
+ } catch (IllegalAccessException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ } catch (InvocationTargetException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ return onCreateView(name, context, attrs);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 7ca54231fe7b..fb2ceac4b810 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,6 +35,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.text.TextPaint;
import android.view.LayoutInflater;
+import android.widget.FrameLayout;
import android.widget.TextClock;
import com.android.systemui.SysuiTestCase;
@@ -51,10 +52,14 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWithLooper
@RunWith(AndroidTestingRunner.class)
+// Need to run on the main thread because KeyguardSliceView$Row init checks for
+// the main thread before acquiring a wake lock. This class is constructed when
+// the keyguard_clcok_switch layout is inflated.
+@RunWithLooper(setAsMainLooper = true)
public class KeyguardClockSwitchTest extends SysuiTestCase {
private PluginManager mPluginManager;
+ private FrameLayout mClockContainer;
@Mock
TextClock mClockView;
@@ -67,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardClockSwitch =
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
+ mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
MockitoAnnotations.initMocks(this);
when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
}
@@ -97,7 +103,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
listener.onPluginConnected(plugin, null);
verify(mClockView).setVisibility(GONE);
- assertThat(plugin.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
}
@Test
@@ -120,7 +126,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
listener.onPluginConnected(plugin2, null);
// THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
- assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
assertThat(plugin1.getView().getParent()).isNull();
}
@@ -161,7 +167,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
// WHEN the first plugin is disconnected
listener.onPluginDisconnected(plugin1);
// THEN the view from the second plugin is still a child of KeyguardClockSwitch.
- assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
assertThat(plugin1.getView().getParent()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c8d137569f8..18bf75e3cfb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -29,9 +29,10 @@ public class TestableDependency extends Dependency {
mComponents = ((SysuiTestableContext) context).getComponents();
}
mContext = context;
- if (SystemUIFactory.getInstance() == null) {
- SystemUIFactory.createFromConfig(context);
- }
+ SystemUIFactory.createFromConfig(context);
+ SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI(this);
start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index b699163c40e8..bb445483c966 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -17,8 +17,10 @@
package com.android.systemui.appops;
import static org.junit.Assert.assertEquals;
+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;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -32,7 +34,6 @@ import android.testing.TestableLooper;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationPresenter;
import org.junit.Before;
import org.junit.Test;
@@ -48,9 +49,12 @@ public class AppOpsControllerTest extends SysuiTestCase {
private static final int TEST_UID = 0;
private static final int TEST_UID_OTHER = 500000;
- @Mock private NotificationPresenter mPresenter;
- @Mock private AppOpsManager mAppOpsManager;
- @Mock private AppOpsController.Callback mCallback;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ private AppOpsController.Callback mCallback;
+ @Mock
+ private AppOpsControllerImpl.H mMockHandler;
private AppOpsControllerImpl mController;
@@ -77,9 +81,13 @@ public class AppOpsControllerTest extends SysuiTestCase {
@Test
public void addCallback_includedCode() {
- mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mController.addCallback(
+ new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
+ mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
}
@@ -106,7 +114,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
@Test
public void addCallback_notSameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
- mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
@@ -128,17 +136,30 @@ public class AppOpsControllerTest extends SysuiTestCase {
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID, TEST_PACKAGE_NAME, true);
- assertEquals(2, mController.getActiveAppOps().size());
+ mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
+ TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+ assertEquals(3, mController.getActiveAppOps().size());
}
- @Test public void getActiveItemsForUser() {
+ @Test
+ public void getActiveItemsForUser() {
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
- assertEquals(1,
+ mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
+ TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+ assertEquals(2,
mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
assertEquals(1,
- mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
+ mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
+ }
+
+ @Test
+ public void opNotedScheduledForRemoval() {
+ mController.setBGHandler(mMockHandler);
+ mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+ verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 24bcca50d34a..563599b32d60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.privacy
import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Intent
+import android.content.pm.UserInfo
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
@@ -30,13 +31,16 @@ import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
@@ -52,8 +56,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
companion object {
val CURRENT_USER_ID = ActivityManager.getCurrentUser()
- val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
const val TAG = "PrivacyItemControllerTest"
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
}
@Mock
@@ -62,6 +66,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
private lateinit var callback: PrivacyItemController.Callback
@Mock
private lateinit var userManager: UserManager
+ @Captor
+ private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -76,8 +82,11 @@ class PrivacyItemControllerTest : SysuiTestCase() {
mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
mContext.addMockSystemService(UserManager::class.java, userManager)
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(listOf(object : UserInfo() {
+ init {
+ id = CURRENT_USER_ID
+ }
+ })).`when`(userManager).getProfiles(anyInt())
privacyItemController = PrivacyItemController(mContext, callback)
}
@@ -100,6 +109,18 @@ class PrivacyItemControllerTest : SysuiTestCase() {
}
@Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 1)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.setListening(true)
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ }
+
+ @Test
fun testRegisterReceiver_allUsers() {
val spiedContext = spy(mContext)
val itemController = PrivacyItemController(spiedContext, callback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index bc7d9836d6f8..ab508a25e76b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,7 +18,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.app.Fragment;
import android.content.Context;
+import android.os.Bundle;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -32,10 +34,13 @@ import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Ignore;
@@ -52,6 +57,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
public QSFragmentTest() {
super(QSFragment.class);
+ injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
}
@Before
@@ -70,7 +76,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mDependency.injectTestDependency(Dependency.BG_LOOPER,
TestableLooper.get(this).getLooper());
mDependency.injectMockDependency(UserSwitcherController.class);
- injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
}
@Test
@@ -116,4 +121,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
assertTrue(qs.isListening());
assertTrue(qs.isExpanded());
}
+
+ @Override
+ protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ return new QSFragment(new RemoteInputQuickSettingsDisabler(context),
+ new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 26fa20de4e86..c3a3e6339daa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -218,4 +218,12 @@ public class TileQueryHelperTest extends SysuiTestCase {
}
assertFalse(specs.contains("other"));
}
+
+ @Test
+ public void testQueryTiles_nullSetting() {
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+ mTileQueryHelper.queryTiles(mQSTileHost);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 5cc3b3c7823b..458377017fb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -26,22 +27,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
-import com.android.systemui.plugins.annotations.Requires;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
import android.app.Activity;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -60,6 +45,21 @@ import android.support.test.annotation.UiThreadTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.Requires;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -79,6 +79,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
private PluginInstanceManager mPluginInstanceManager;
private PluginManagerImpl mMockManager;
private VersionInfo mMockVersionInfo;
+ private PluginEnabler mMockEnabler;
+ ComponentName mTestPluginComponentName =
+ new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
@Before
public void setup() throws Exception {
@@ -88,9 +91,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
mMockManager = mock(PluginManagerImpl.class);
- when(mMockManager.getClassLoader(any(), any()))
- .thenReturn(getClass().getClassLoader());
- when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm));
+ when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+ mMockEnabler = mock(PluginEnabler.class);
+ when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
mMockVersionInfo = mock(VersionInfo.class);
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
@@ -230,18 +233,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
// Start with an unrelated class.
boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
assertFalse(result);
- verify(mMockPm, never()).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt());
// Now hand it a real class and make sure it disables the plugin.
result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName());
assertTrue(result);
- verify(mMockPm).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler).setDisabled(
+ mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
}
@Test
@@ -250,10 +248,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.disableAll();
- verify(mMockPm).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler).setDisabled(
+ mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
}
@Test
@@ -275,8 +271,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
List<ResolveInfo> list = new ArrayList<>();
ResolveInfo info = new ResolveInfo();
info.serviceInfo = mock(ServiceInfo.class);
- info.serviceInfo.packageName = "com.android.systemui";
- info.serviceInfo.name = TestPlugin.class.getName();
+ info.serviceInfo.packageName = mTestPluginComponentName.getPackageName();
+ info.serviceInfo.name = mTestPluginComponentName.getClassName();
when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin");
list.add(info);
when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list);
@@ -288,6 +284,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
ApplicationInfo appInfo = getContext().getApplicationInfo();
when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
appInfo);
+ when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
}
private void createPlugin() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index ff1bc8abf5d1..76e68f1df724 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -27,7 +27,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -82,17 +81,18 @@ public class PluginManagerTest extends SysuiTestCase {
.thenReturn(mMockPluginInstance);
mMockPackageManager = mock(PackageManager.class);
- mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+ mPluginManager = new PluginManagerImpl(
+ getContext(), mMockFactory, true,
mMockExceptionHandler, new PluginInitializerImpl() {
- @Override
- public String[] getWhitelistedPlugins(Context context) {
- return new String[0];
- }
-
- @Override
- public PluginEnabler getPluginEnabler(Context context) {
- return new PluginEnablerImpl(mMockPackageManager);
- }
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+
+ @Override
+ public PluginEnabler getPluginEnabler(Context context) {
+ return new PluginEnablerImpl(context, mMockPackageManager);
+ }
});
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index f8ff583162cf..894ef3dde779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,6 +26,7 @@ import android.testing.TestableLooper;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -55,7 +56,8 @@ import org.mockito.MockitoAnnotations;
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
+ @Mock
+ private NotificationEntryListener mEntryManagerCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 8d52ccd71808..14e611a26b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,7 @@ public class SmartReplyControllerTest extends SysuiTestCase {
private static final String TEST_CHOICE_TEXT = "A Reply";
private static final int TEST_CHOICE_INDEX = 2;
private static final int TEST_CHOICE_COUNT = 4;
+ private static final int TEST_ACTION_COUNT = 3;
private Notification mNotification;
private NotificationData.Entry mEntry;
@@ -117,12 +118,14 @@ public class SmartReplyControllerTest extends SysuiTestCase {
}
@Test
- public void testShowSmartReply_logsToStatusBar() throws RemoteException {
- mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
+ public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException {
+ final boolean generatedByAsssistant = true;
+ mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT,
+ generatedByAsssistant);
// Check we log the result to the status bar service.
- verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(),
- TEST_CHOICE_COUNT);
+ verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(),
+ TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index def7513bc7dd..871ff8998c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -29,8 +29,6 @@ import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -50,7 +48,6 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -105,6 +102,7 @@ public class NotificationDataTest extends SysuiTestCase {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+ when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -129,41 +127,6 @@ public class NotificationDataTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
- initStatusBarNotification(false);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
- initStatusBarNotification(true);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
- initStatusBarNotification(true);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertTrue(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
public void testChannelSetWhenAdded() {
mNotificationData.add(mRow.getEntry());
assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
@@ -230,76 +193,6 @@ public class NotificationDataTest extends SysuiTestCase {
}
@Test
- public void testSuppressSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressSystemAlertNotification() {
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressMalformedSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
- // missing extra
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
- sbn.getNotification().extras = bundle;
-
- // extra missing values
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testShouldFilterHiddenNotifications() {
- initStatusBarNotification(false);
- // setup
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- // test should filter out hidden notifications:
- // hidden
- when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertTrue(mNotificationData.shouldFilterOut(entry));
-
- // not hidden
- when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
- entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertFalse(mNotificationData.shouldFilterOut(entry));
- }
-
- @Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
@@ -325,9 +218,10 @@ public class NotificationDataTest extends SysuiTestCase {
Notification n = mMockStatusBarNotification.getNotification();
n.flags = Notification.FLAG_FOREGROUND_SERVICE;
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -341,9 +235,10 @@ public class NotificationDataTest extends SysuiTestCase {
n = nb.build();
when(mMockStatusBarNotification.getNotification()).thenReturn(n);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -353,9 +248,10 @@ public class NotificationDataTest extends SysuiTestCase {
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -365,31 +261,33 @@ public class NotificationDataTest extends SysuiTestCase {
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
+
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+ assertTrue(entry.shouldSuppressAmbient());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
}
@Test
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 904e5b99b0ab..701ea7d3e2fe 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
@@ -103,7 +103,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
+ @Mock
+ private NotificationRowBinder.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationListenerService.RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
@@ -135,7 +138,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
super(context);
mBarService = barService;
mCountDownLatch = new CountDownLatch(1);
- mUseHeadsUp = true;
}
@Override
@@ -232,6 +234,11 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+ NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class);
+ notificationRowBinder.setUpWithPresenter(
+ mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
+ notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
+
setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -243,7 +250,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
doAnswer(invocation -> {
mCountDownLatch.countDown();
return null;
- }).when(mCallback).onBindRow(any(), any(), any(), any());
+ }).when(mBindCallback).onBindRow(any(), any(), any(), any());
// Post on main thread, otherwise we will be stuck waiting here for the inflation finished
// callback forever, since it won't execute until the tests ends.
@@ -260,7 +267,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
// Row inflation:
ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
NotificationData.Entry.class);
- verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
+ verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
NotificationData.Entry entry = entryCaptor.getValue();
verify(mRemoteInputManager).bindRow(entry.getRow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
new file mode 100644
index 000000000000..da8bc01dc29d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+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)
+@RunWithLooper
+public class NotificationFilterTest extends SysuiTestCase {
+
+ private static final int UID_NORMAL = 123;
+ private static final int UID_ALLOW_DURING_SETUP = 456;
+ private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+
+ private final StatusBarNotification mMockStatusBarNotification =
+ mock(StatusBarNotification.class);
+
+ @Mock
+ ForegroundServiceController mFsc;
+ @Mock
+ NotificationData.KeyguardEnvironment mEnvironment;
+ private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+
+ private NotificationFilter mNotificationFilter;
+ private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ MockitoAnnotations.initMocks(this);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_NORMAL)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_ALLOW_DURING_SETUP)))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ mDependency.injectTestDependency(NotificationGroupManager.class,
+ new NotificationGroupManager());
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+ when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+ when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+ mRow = new NotificationTestHelper(getContext()).createRow();
+ mNotificationFilter = new NotificationFilter();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
+ initStatusBarNotification(true);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
+ initStatusBarNotification(true);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertTrue(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ public void testSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressSystemAlertNotification() {
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressMalformedSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+ // missing extra
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
+ sbn.getNotification().extras = bundle;
+
+ // extra missing values
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testShouldFilterHiddenNotifications() {
+ initStatusBarNotification(false);
+ // setup
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ // test should filter out hidden notifications:
+ // hidden
+ NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = true;
+ assertTrue(mNotificationFilter.shouldFilterOut(entry));
+
+ // not hidden
+ entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = false;
+ assertFalse(mNotificationFilter.shouldFilterOut(entry));
+ }
+
+ private void initStatusBarNotification(boolean allowDuringSetup) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+ when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+ }
+}
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 e65e806da93a..b4f99c4ed156 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
@@ -126,7 +126,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mPowerManager = new PowerManager(mContext, powerManagerService,
Handler.createAsync(Looper.myLooper()));
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager,
mContext);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 9e2db913aba5..728723b4094c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,10 +14,13 @@
package com.android.systemui.statusbar.phone;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.Fragment;
import android.content.Context;
+import android.os.Bundle;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -27,13 +30,17 @@ import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +51,23 @@ import org.junit.runner.RunWith;
@SmallTest
public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
+ private OverviewProxyService mOverviewProxyService =
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ private AccessibilityManagerWrapper mAccessibilityWrapper =
+ new AccessibilityManagerWrapper(mContext) {
+ Tracker mTracker = mLeakCheck.getTracker("accessibility_manager");
+
+ @Override
+ public void addCallback(AccessibilityServicesStateChangeListener listener) {
+ mTracker.getLeakInfo(listener).addAllocation(new Throwable());
+ }
+
+ @Override
+ public void removeCallback(AccessibilityServicesStateChangeListener listener) {
+ mTracker.getLeakInfo(listener).clearAllocations();
+ }
+ };
+
public NavigationBarFragmentTest() {
super(NavigationBarFragment.class);
}
@@ -54,32 +78,19 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
- mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiContext.putComponent(Recents.class, mock(Recents.class));
mSysuiContext.putComponent(Divider.class, mock(Divider.class));
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- mDependency.injectMockDependency(OverviewProxyService.class);
WindowManager windowManager = mock(WindowManager.class);
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
when(windowManager.getDefaultDisplay()).thenReturn(
defaultDisplay);
mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
- Tracker tracker = mLeakCheck.getTracker("accessibility_manager");
- AccessibilityManagerWrapper wrapper = new AccessibilityManagerWrapper(mContext) {
- @Override
- public void addCallback(AccessibilityServicesStateChangeListener listener) {
- tracker.getLeakInfo(listener).addAllocation(new Throwable());
- }
-
- @Override
- public void removeCallback(AccessibilityServicesStateChangeListener listener) {
- tracker.getLeakInfo(listener).clearAllocations();
- }
- };
- mDependency.injectTestDependency(AccessibilityManagerWrapper.class, wrapper);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper);
}
@Test
@@ -91,4 +102,15 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
}
+ @Override
+ protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ DeviceProvisionedController deviceProvisionedController =
+ new DeviceProvisionedControllerImpl(context);
+ assertNotNull(mAccessibilityWrapper);
+ return new NavigationBarFragment(mAccessibilityWrapper,
+ deviceProvisionedController,
+ new MetricsLogger(),
+ new AssistManager(deviceProvisionedController, mContext),
+ mOverviewProxyService);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index ee39e10b4165..490288efd40f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,9 +32,9 @@ import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -61,8 +61,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
private AmbientPulseManager mAmbientPulseManager;
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
- @Captor private ArgumentCaptor<AlertTransferListener> mListenerCaptor;
- private AlertTransferListener mAlertTransferListener;
+ @Captor
+ private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
@@ -85,8 +86,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
- verify(mNotificationEntryManager).setAlertTransferListener(mListenerCaptor.capture());
- mAlertTransferListener = mListenerCaptor.getValue();
+ verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
+ mNotificationEntryListener = mListenerCaptor.getValue();
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
}
@@ -121,7 +122,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// The alert state should transfer back to the summary as there is now more than one
@@ -148,7 +149,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Dozing changed so no reason to re-alert summary.
@@ -186,7 +187,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
@@ -210,13 +211,13 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
.getContentFlag());
@@ -236,7 +237,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mAlertTransferListener.onEntryRemoved(childEntry);
+ mNotificationEntryListener.onEntryRemoved(childEntry);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c207feff26f3..c584d026f62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -91,7 +92,10 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+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.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -129,6 +133,8 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
+ @Mock
+ private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
// Mock dependencies:
@Mock private NotificationViewHierarchyManager mViewHierarchyManager;
@@ -141,13 +147,17 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private NotificationPresenter mNotificationPresenter;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
@Mock private BubbleController mBubbleController;
+ @Mock
+ private NotificationFilter mNotificationFilter;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationEntryManager mEntryManager;
+ private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private NotificationLogger mNotificationLogger;
private CommandQueue mCommandQueue;
@@ -168,6 +178,17 @@ public class StatusBarTest extends SysuiTestCase {
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService,
+ Handler.createAsync(Looper.myLooper()));
+
+ mNotificationInterruptionStateProvider =
+ new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+ mDreamManager);
+ mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -176,10 +197,7 @@ public class StatusBarTest extends SysuiTestCase {
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
mNotificationLogger = new NotificationLogger();
-
- IPowerManager powerManagerService = mock(IPowerManager.class);
- mPowerManager = new PowerManager(mContext, powerManagerService,
- Handler.createAsync(Looper.myLooper()));
+ DozeLog.traceDozing(mContext, false /* dozing */);
mCommandQueue = mock(CommandQueue.class);
when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -204,7 +222,10 @@ public class StatusBarTest extends SysuiTestCase {
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+ mHeadsUpManager, mHeadsUpSuppressor);
+
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -361,11 +382,9 @@ public class StatusBarTest extends SysuiTestCase {
public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -375,19 +394,18 @@ public class StatusBarTest extends SysuiTestCase {
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -397,46 +415,44 @@ public class StatusBarTest extends SysuiTestCase {
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -724,20 +740,29 @@ public class StatusBarTest extends SysuiTestCase {
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(IDreamManager dreamManager,
- PowerManager powerManager, Context context) {
+ public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
super(context);
- mDreamManager = dreamManager;
mPowerManager = powerManager;
}
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
- Callback callback,
+ NotificationEntryListener callback,
HeadsUpManagerPhone headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
+ }
+ }
+
+ public static class TestableNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+
+ public TestableNotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ super(context, powerManager, dreamManager);
mUseHeadsUp = true;
}
}
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index d316fbd8907c..b81ae5bbe3fa 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorBlack
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index afc42873a4d6..db92157c8fdf 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorGreen
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index 336616921d71..d7dc4978e2ca 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorPurple
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/CleanSpec.mk b/packages/overlays/CleanSpec.mk
new file mode 100644
index 000000000000..16fbaa202aa1
--- /dev/null
+++ b/packages/overlays/CleanSpec.mk
@@ -0,0 +1,53 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/AccentColor*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutout*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/IconShape*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index 74c43b40616f..bf2b6312d68f 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index d83b30a8785a..70429064ec2b 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f5afad24676f..ae69e1137e60 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f1f8c27d94f1..7dcadfbd4708 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationTall
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index d149d8ecf4df..3f7be73aa5fa 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationWide
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index a734a6b46947..08428d192fae 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeRoundedRect
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index 217da9feb534..ceb745ae1429 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquare
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index fd3bfa06de83..34edc3b78b09 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquircle
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index ea43423f93ba..834a1c357c61 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeTeardrop
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 66d64b1a5f7a..529d78f577ed 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6649,6 +6649,21 @@ message MetricsEvent {
// OS: Q
QS_SENSOR_PRIVACY = 1598;
+ // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart actions.
+ // OS: Q
+ NOTIFICATION_SMART_ACTION_COUNT = 1599;
+
+ // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
+ // Whether the notification has notification-assistant generated
+ // actions/replies.
+ // OS: Q
+ NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED = 1600;
+
+ // Tagged data for NOTIFICATION_ITEM_ACTION. Whether the action is a smart
+ // action.
+ // OS: Q
+ NOTIFICATION_ACTION_IS_SMART = 1601;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index fc7265db1be2..681a9941faea 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -38,8 +38,8 @@ import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
import com.android.internal.os.IResultReceiver;
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteAugmentedAutofillService
extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 417ea9c84393..e5529aff7c62 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -39,7 +39,7 @@ import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteFillService
extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2cbab492a605..a9f4e46395f3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,10 +16,13 @@
package com.android.server.backup;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
@@ -41,6 +44,7 @@ import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
@@ -83,22 +87,27 @@ public class BackupManagerService {
}
private final Context mContext;
- private UserBackupManagerService mUserBackupManagerService;
+ private final Trampoline mTrampoline;
+ private final HandlerThread mBackupThread;
+
+ // Keeps track of all unlocked users registered with this service. Indexed by user id.
+ private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+
+ private Set<ComponentName> mTransportWhitelist;
/** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context, Trampoline trampoline, HandlerThread backupThread) {
- // Set up our transport options and initialize the default transport
+ mContext = checkNotNull(context);
+ mTrampoline = checkNotNull(trampoline);
+ mBackupThread = checkNotNull(backupThread);
+
+ // Set up our transport options.
SystemConfig systemConfig = SystemConfig.getInstance();
- Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
- if (transportWhitelist == null) {
- transportWhitelist = Collections.emptySet();
+ mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (mTransportWhitelist == null) {
+ mTransportWhitelist = Collections.emptySet();
}
-
- mContext = context;
- mUserBackupManagerService =
- UserBackupManagerService.createAndInitializeService(
- context, trampoline, backupThread, transportWhitelist);
}
/**
@@ -108,19 +117,13 @@ public class BackupManagerService {
* @param userId User id on which the backup operation is being requested.
* @param message A message to include in the exception if it is thrown.
*/
- private void enforceCallingPermissionOnUserId(int userId, String message) {
+ private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
if (Binder.getCallingUserHandle().getIdentifier() != userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
}
}
- // TODO(b/118520567): Remove when tests are modified to use per-user instance.
- @VisibleForTesting
- void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
- mUserBackupManagerService = userBackupManagerService;
- }
-
/**
* Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
* a background thread to keep the unlock time down.
@@ -139,9 +142,47 @@ public class BackupManagerService {
* Starts the backup service for user {@code userId} by creating a new instance of {@link
* UserBackupManagerService} and registering it with this service.
*/
- // TODO(b/120212806): Add UserBackupManagerService initialization logic.
- void startServiceForUser(int userId) {
- // Intentionally empty.
+ @VisibleForTesting
+ protected void startServiceForUser(int userId) {
+ UserBackupManagerService userBackupManagerService =
+ UserBackupManagerService.createAndInitializeService(
+ userId, mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+ startServiceForUser(userId, userBackupManagerService);
+ }
+
+ /**
+ * Starts the backup service for user {@code userId} by registering its instance of {@link
+ * UserBackupManagerService} with this service.
+ */
+ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+ mServiceUsers.put(userId, userBackupManagerService);
+ }
+
+ SparseArray<UserBackupManagerService> getServiceUsers() {
+ return mServiceUsers;
+ }
+
+ /**
+ * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+ * If the user is not registered with the service (either the user is locked or not eligible for
+ * the backup service) then return {@code null}.
+ *
+ * @param userId The id of the user to retrieve its instance of {@link
+ * UserBackupManagerService}.
+ * @param caller A {@link String} identifying the caller for logging purposes.
+ * @throws SecurityException if {@code userId} is different from the calling user id and the
+ * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Nullable
+ @VisibleForTesting
+ UserBackupManagerService getServiceForUserIfCallerHasPermission(
+ @UserIdInt int userId, String caller) {
+ enforceCallingPermissionOnUserId(userId, caller);
+ UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
+ if (userBackupManagerService == null) {
+ Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+ }
+ return userBackupManagerService;
}
/*
@@ -149,7 +190,7 @@ public class BackupManagerService {
* They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
* action on the passed in user. Currently this is a straight redirection (see TODO).
*/
- // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+ // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
// ---------------------------------------------
// BACKUP AGENT OPERATIONS
@@ -160,32 +201,52 @@ public class BackupManagerService {
* backup for their app {@code packageName}. Only used for apps participating in key-value
* backup.
*/
- public void dataChanged(String packageName) {
- mUserBackupManagerService.dataChanged(packageName);
+ public void dataChanged(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dataChanged(packageName);
+ }
}
/**
* Callback: a requested backup agent has been instantiated. This should only be called from the
* {@link ActivityManager}.
*/
- public void agentConnected(String packageName, IBinder agentBinder) {
- mUserBackupManagerService.agentConnected(packageName, agentBinder);
+ public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentConnected(packageName, agentBinder);
+ }
}
/**
* Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
* called from the {@link ActivityManager}.
*/
- public void agentDisconnected(String packageName) {
- mUserBackupManagerService.agentDisconnected(packageName);
+ public void agentDisconnected(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentDisconnected(packageName);
+ }
}
/**
* Used by a currently-active backup agent to notify the service that it has completed its given
* outstanding asynchronous backup/restore operation.
*/
- public void opComplete(int token, long result) {
- mUserBackupManagerService.opComplete(token, result);
+ public void opComplete(@UserIdInt int userId, int token, long result) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "opComplete()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.opComplete(token, result);
+ }
}
// ---------------------------------------------
@@ -193,44 +254,87 @@ public class BackupManagerService {
// ---------------------------------------------
/** Run an initialize operation for the given transports {@code transportNames}. */
- public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mUserBackupManagerService.initializeTransports(transportNames, observer);
+ public void initializeTransports(
+ @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.initializeTransports(transportNames, observer);
+ }
}
/**
* Clear the given package {@code packageName}'s backup data from the transport {@code
* transportName}.
*/
- public void clearBackupData(String transportName, String packageName) {
- mUserBackupManagerService.clearBackupData(transportName, packageName);
+ public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.clearBackupData(transportName, packageName);
+ }
}
/** Return the name of the currently active transport. */
- public String getCurrentTransport() {
- return mUserBackupManagerService.getCurrentTransport();
+ @Nullable
+ public String getCurrentTransport(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransport();
}
/**
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
- public ComponentName getCurrentTransportComponent() {
- return mUserBackupManagerService.getCurrentTransportComponent();
+ @Nullable
+ public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransportComponent();
}
/** Report all known, available backup transports by name. */
- public String[] listAllTransports() {
- return mUserBackupManagerService.listAllTransports();
+ @Nullable
+ public String[] listAllTransports(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransports();
}
/** Report all known, available backup transports by {@link ComponentName}. */
- public ComponentName[] listAllTransportComponents() {
- return mUserBackupManagerService.listAllTransportComponents();
+ @Nullable
+ public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransportComponents();
}
/** Report all system whitelisted transports. */
+ @Nullable
public String[] getTransportWhitelist() {
- return mUserBackupManagerService.getTransportWhitelist();
+ // No permission check, intentionally.
+ String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+ int i = 0;
+ for (ComponentName component : mTransportWhitelist) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
}
/**
@@ -257,19 +361,25 @@ public class BackupManagerService {
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
public void updateTransportAttributes(
+ @UserIdInt int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
- mUserBackupManagerService.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
}
/**
@@ -280,8 +390,13 @@ public class BackupManagerService {
*/
@Deprecated
@Nullable
- public String selectBackupTransport(String transportName) {
- return mUserBackupManagerService.selectBackupTransport(transportName);
+ public String selectBackupTransport(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.selectBackupTransport(transportName);
}
/**
@@ -289,8 +404,15 @@ public class BackupManagerService {
* with the result upon completion.
*/
public void selectBackupTransportAsync(
- ComponentName transportComponent, ISelectBackupTransportCallback listener) {
- mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ @UserIdInt int userId,
+ ComponentName transportComponent,
+ ISelectBackupTransportCallback listener) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ }
}
/**
@@ -298,8 +420,14 @@ public class BackupManagerService {
* available transports, or if the transport does not supply any configuration UI, the method
* returns {@code null}.
*/
- public Intent getConfigurationIntent(String transportName) {
- return mUserBackupManagerService.getConfigurationIntent(transportName);
+ @Nullable
+ public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getConfigurationIntent(transportName);
}
/**
@@ -311,21 +439,39 @@ public class BackupManagerService {
* @param transportName The name of the registered transport.
* @return The current destination string or null if the transport is not registered.
*/
- public String getDestinationString(String transportName) {
- return mUserBackupManagerService.getDestinationString(transportName);
+ @Nullable
+ public String getDestinationString(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDestinationString(transportName);
}
/** Supply the manage-data intent for the given transport. */
- public Intent getDataManagementIntent(String transportName) {
- return mUserBackupManagerService.getDataManagementIntent(transportName);
+ @Nullable
+ public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementIntent(transportName);
}
/**
* Supply the menu label for affordances that fire the manage-data intent for the given
* transport.
*/
- public String getDataManagementLabel(String transportName) {
- return mUserBackupManagerService.getDataManagementLabel(transportName);
+ @Nullable
+ public String getDataManagementLabel(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementLabel(transportName);
}
// ---------------------------------------------
@@ -334,26 +480,32 @@ public class BackupManagerService {
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
- mUserBackupManagerService.setBackupEnabled(enable);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setBackupEnabled(enable);
+ }
}
/** Enable/disable automatic restore of app data at install time. */
- public void setAutoRestore(boolean autoRestore) {
- mUserBackupManagerService.setAutoRestore(autoRestore);
- }
+ public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
- /** Mark the backup service as having been provisioned (device has gone through SUW). */
- public void setBackupProvisioned(boolean provisioned) {
- mUserBackupManagerService.setBackupProvisioned(provisioned);
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setAutoRestore(autoRestore);
+ }
}
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
public boolean isBackupEnabled(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
- return mUserBackupManagerService.isBackupEnabled();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
+
+ return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
// ---------------------------------------------
@@ -361,15 +513,25 @@ public class BackupManagerService {
// ---------------------------------------------
/** Checks if the given package {@code packageName} is eligible for backup. */
- public boolean isAppEligibleForBackup(String packageName) {
- return mUserBackupManagerService.isAppEligibleForBackup(packageName);
+ public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.isAppEligibleForBackup(packageName);
}
/**
* Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
- public String[] filterAppsEligibleForBackup(String[] packages) {
- return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
+ @Nullable
+ public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.filterAppsEligibleForBackup(packages);
}
/**
@@ -377,8 +539,12 @@ public class BackupManagerService {
* they have pending updates.
*/
public void backupNow(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "backupNow");
- mUserBackupManagerService.backupNow();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "backupNow()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.backupNow();
+ }
}
/**
@@ -391,14 +557,22 @@ public class BackupManagerService {
IBackupObserver observer,
IBackupManagerMonitor monitor,
int flags) {
- enforceCallingPermissionOnUserId(userId, "requestBackup");
- return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
+
+ return userBackupManagerService == null
+ ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
/** Cancel all running backup operations. */
public void cancelBackups(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "cancelBackups");
- mUserBackupManagerService.cancelBackups();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.cancelBackups();
+ }
}
/**
@@ -410,7 +584,11 @@ public class BackupManagerService {
* return value to the callback {@link JobService#onStartJob(JobParameters)}.
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
- return mUserBackupManagerService.beginFullBackup(scheduledJob);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.beginFullBackup(scheduledJob);
}
/**
@@ -418,14 +596,24 @@ public class BackupManagerService {
* longer met for running the full backup job.
*/
public void endFullBackup() {
- mUserBackupManagerService.endFullBackup();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.endFullBackup();
+ }
}
/**
* Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
*/
- public void fullTransportBackup(String[] packageNames) {
- mUserBackupManagerService.fullTransportBackup(packageNames);
+ public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.fullTransportBackup(packageNames);
+ }
}
// ---------------------------------------------
@@ -436,24 +624,41 @@ public class BackupManagerService {
* Used to run a restore pass for an application that is being installed. This should only be
* called from the {@link PackageManager}.
*/
- public void restoreAtInstall(String packageName, int token) {
- mUserBackupManagerService.restoreAtInstall(packageName, token);
+ public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.restoreAtInstall(packageName, token);
+ }
}
/**
* Begin a restore for the specified package {@code packageName} using the specified transport
* {@code transportName}.
*/
- public IRestoreSession beginRestoreSession(String packageName, String transportName) {
- return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
+ @Nullable
+ public IRestoreSession beginRestoreSession(
+ @UserIdInt int userId, String packageName, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.beginRestoreSession(packageName, transportName);
}
/**
* Get the restore-set token for the best-available restore set for this {@code packageName}:
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
- public long getAvailableRestoreToken(String packageName) {
- return mUserBackupManagerService.getAvailableRestoreToken(packageName);
+ public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
+
+ return userBackupManagerService == null
+ ? 0
+ : userBackupManagerService.getAvailableRestoreToken(packageName);
}
// ---------------------------------------------
@@ -462,12 +667,21 @@ public class BackupManagerService {
/** Sets the backup password used when running adb backup. */
public boolean setBackupPassword(String currentPassword, String newPassword) {
- return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
/** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
public boolean hasBackupPassword() {
- return mUserBackupManagerService.hasBackupPassword();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+ return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
}
/**
@@ -488,19 +702,22 @@ public class BackupManagerService {
boolean doCompress,
boolean doKeyValue,
String[] packageNames) {
- enforceCallingPermissionOnUserId(userId, "adbBackup");
-
- mUserBackupManagerService.adbBackup(
- fd,
- includeApks,
- includeObbs,
- includeShared,
- doWidgets,
- doAllApps,
- includeSystem,
- doCompress,
- doKeyValue,
- packageNames);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbBackup(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ doCompress,
+ doKeyValue,
+ packageNames);
+ }
}
/**
@@ -509,9 +726,12 @@ public class BackupManagerService {
* requires on-screen confirmation by the user.
*/
public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
- mUserBackupManagerService.adbRestore(fd);
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbRestore(fd);
+ }
}
/**
@@ -519,13 +739,19 @@ public class BackupManagerService {
* to require a user-facing disclosure about the operation.
*/
public void acknowledgeAdbBackupOrRestore(
+ @UserIdInt int userId,
int token,
boolean allow,
String currentPassword,
String encryptionPassword,
IFullBackupRestoreObserver observer) {
- mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
- token, allow, currentPassword, encryptionPassword, observer);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.acknowledgeAdbBackupOrRestore(
+ token, allow, currentPassword, encryptionPassword, observer);
+ }
}
// ---------------------------------------------
@@ -534,7 +760,12 @@ public class BackupManagerService {
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mUserBackupManagerService.dump(fd, pw, args);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
}
private static boolean readBackupEnableState(int userId) {
@@ -592,7 +823,7 @@ public class BackupManagerService {
if (userId == UserHandle.USER_SYSTEM) {
sInstance.initializeServiceAndUnlockSystemUser();
} else {
- sInstance.startServiceForUser(userId);
+ sInstance.unlockUser(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 4acd5c494ac7..d40368025c0c 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -116,7 +116,7 @@ public class Trampoline extends IBackupManager.Stub {
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- protected boolean isMultiUserEnabled() {
+ private boolean isMultiUserEnabled() {
return Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.BACKUP_MULTI_USER_ENABLED,
@@ -145,6 +145,10 @@ public class Trampoline extends IBackupManager.Stub {
return new BackupManagerService(mContext, this, mHandlerThread);
}
+ protected void postToHandler(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
/**
* Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
* system user can initialize the service.
@@ -174,14 +178,18 @@ public class Trampoline extends IBackupManager.Stub {
* to initialize {@link BackupManagerService} and set backup state for the system user.
*/
void initializeServiceAndUnlockSystemUser() {
- mHandler.post(
+ postToHandler(
() -> {
+ // Initialize the backup service.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
initializeService(UserHandle.USER_SYSTEM);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // Start the service for the system user.
BackupManagerService service = mService;
if (service != null) {
+ Slog.i(TAG, "Starting service for system user");
+ service.startServiceForUser(UserHandle.USER_SYSTEM);
Slog.i(TAG, "Unlocking system user");
service.unlockSystemUser();
}
@@ -195,20 +203,21 @@ public class Trampoline extends IBackupManager.Stub {
*/
// TODO(b/120212806): Consolidate service start for system and non-system users when system
// user-only logic is removed.
- void startServiceForUser(int userId) {
+ void unlockUser(int userId) {
if (!isMultiUserEnabled()) {
Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
return;
}
- mHandler.post(
- () -> {
- BackupManagerService service = mService;
- if (service != null) {
- Slog.i(TAG, "Starting service for user: " + userId);
- service.startServiceForUser(userId);
- }
- });
+ postToHandler(() -> startServiceForUser(userId));
+ }
+
+ private void startServiceForUser(int userId) {
+ BackupManagerService service = mService;
+ if (service != null) {
+ Slog.i(TAG, "Starting service for user: " + userId);
+ service.startServiceForUser(userId);
+ }
}
/**
@@ -242,6 +251,7 @@ public class Trampoline extends IBackupManager.Stub {
if (makeActive) {
mService = createBackupManagerService();
mSuppressFile.delete();
+ startServiceForUser(userId);
} else {
mService = null;
try {
@@ -274,53 +284,81 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
+ public void dataChangedForUser(int userId, String packageName) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.dataChanged(userId, packageName);
+ }
+ }
+
+ @Override
public void dataChanged(String packageName) throws RemoteException {
+ dataChangedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ @Override
+ public void initializeTransportsForUser(
+ int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.dataChanged(packageName);
+ svc.initializeTransports(userId, transportNames, observer);
}
}
@Override
- public void initializeTransports(String[] transportNames, IBackupObserver observer)
+ public void clearBackupDataForUser(int userId, String transportName, String packageName)
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.initializeTransports(transportNames, observer);
+ svc.clearBackupData(userId, transportName, packageName);
}
}
@Override
public void clearBackupData(String transportName, String packageName)
throws RemoteException {
+ clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+ }
+
+ @Override
+ public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.clearBackupData(transportName, packageName);
+ svc.agentConnected(userId, packageName, agent);
}
}
@Override
public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+ agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+ }
+
+ @Override
+ public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentConnected(packageName, agent);
+ svc.agentDisconnected(userId, packageName);
}
}
@Override
public void agentDisconnected(String packageName) throws RemoteException {
+ agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ @Override
+ public void restoreAtInstallForUser(int userId, String packageName, int token)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentDisconnected(packageName);
+ svc.restoreAtInstall(userId, packageName, token);
}
}
@Override
public void restoreAtInstall(String packageName, int token) throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.restoreAtInstall(packageName, token);
- }
+ restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
}
@Override
@@ -338,19 +376,23 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.setAutoRestore(doAutoRestore);
+ svc.setAutoRestore(userId, doAutoRestore);
}
}
@Override
+ public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
+ }
+
+ @Override
public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.setBackupProvisioned(isProvisioned);
- }
+ /*
+ * This is now a no-op; provisioning is simply the device's own setup state.
+ */
}
@Override
@@ -401,10 +443,11 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void fullTransportBackup(String[] packageNames) throws RemoteException {
+ public void fullTransportBackupForUser(int userId, String[] packageNames)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.fullTransportBackup(packageNames);
+ svc.fullTransportBackup(userId, packageNames);
}
}
@@ -417,20 +460,40 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
- String encryptionPassword, IFullBackupRestoreObserver observer)
- throws RemoteException {
+ public void acknowledgeFullBackupOrRestoreForUser(
+ int userId,
+ int token,
+ boolean allow,
+ String curPassword,
+ String encryptionPassword,
+ IFullBackupRestoreObserver observer)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.acknowledgeAdbBackupOrRestore(token, allow,
+ svc.acknowledgeAdbBackupOrRestore(userId, token, allow,
curPassword, encryptionPassword, observer);
}
}
@Override
- public String getCurrentTransport() throws RemoteException {
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+ String encryptionPassword, IFullBackupRestoreObserver observer)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransport() : null;
+ acknowledgeFullBackupOrRestoreForUser(
+ binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+ }
+
+
+ @Override
+ public String getCurrentTransportForUser(int userId) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getCurrentTransport(userId) : null;
+ }
+
+ @Override
+ public String getCurrentTransport() throws RemoteException {
+ return getCurrentTransportForUser(binderGetCallingUserId());
}
/**
@@ -439,21 +502,26 @@ public class Trampoline extends IBackupManager.Stub {
*/
@Override
@Nullable
- public ComponentName getCurrentTransportComponent() {
+ public ComponentName getCurrentTransportComponentForUser(int userId) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransportComponent() : null;
+ return (svc != null) ? svc.getCurrentTransportComponent(userId) : null;
}
@Override
- public String[] listAllTransports() throws RemoteException {
+ public String[] listAllTransportsForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransports() : null;
+ return (svc != null) ? svc.listAllTransports(userId) : null;
}
@Override
- public ComponentName[] listAllTransportComponents() throws RemoteException {
+ public String[] listAllTransports() throws RemoteException {
+ return listAllTransportsForUser(binderGetCallingUserId());
+ }
+
+ @Override
+ public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransportComponents() : null;
+ return (svc != null) ? svc.listAllTransportComponents(userId) : null;
}
@Override
@@ -463,7 +531,8 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void updateTransportAttributes(
+ public void updateTransportAttributesForUser(
+ int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
@@ -473,6 +542,7 @@ public class Trampoline extends IBackupManager.Stub {
BackupManagerService svc = mService;
if (svc != null) {
svc.updateTransportAttributes(
+ userId,
transportComponent,
name,
configurationIntent,
@@ -483,17 +553,23 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public String selectBackupTransport(String transport) throws RemoteException {
+ public String selectBackupTransportForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.selectBackupTransport(transport) : null;
+ return (svc != null) ? svc.selectBackupTransport(userId, transport) : null;
+ }
+
+ @Override
+ public String selectBackupTransport(String transport) throws RemoteException {
+ return selectBackupTransportForUser(binderGetCallingUserId(), transport);
}
@Override
- public void selectBackupTransportAsync(ComponentName transport,
+ public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
ISelectBackupTransportCallback listener) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.selectBackupTransportAsync(transport, listener);
+ svc.selectBackupTransportAsync(userId, transport, listener);
} else {
if (listener != null) {
try {
@@ -506,60 +582,86 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public Intent getConfigurationIntent(String transport) throws RemoteException {
+ public Intent getConfigurationIntentForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getConfigurationIntent(transport) : null;
+ return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null;
}
@Override
- public String getDestinationString(String transport) throws RemoteException {
+ public Intent getConfigurationIntent(String transport)
+ throws RemoteException {
+ return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDestinationString(transport) : null;
+ return (svc != null) ? svc.getDestinationString(userId, transport) : null;
+ }
+
+ @Override
+ public String getDestinationString(String transport) throws RemoteException {
+ return getDestinationStringForUser(binderGetCallingUserId(), transport);
}
@Override
- public Intent getDataManagementIntent(String transport) throws RemoteException {
+ public Intent getDataManagementIntentForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementIntent(transport) : null;
+ return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null;
}
@Override
- public String getDataManagementLabel(String transport) throws RemoteException {
+ public Intent getDataManagementIntent(String transport)
+ throws RemoteException {
+ return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public String getDataManagementLabelForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementLabel(transport) : null;
+ return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null;
}
@Override
- public IRestoreSession beginRestoreSession(String packageName, String transportID)
+ public String getDataManagementLabel(String transport)
throws RemoteException {
+ return getDataManagementLabelForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public IRestoreSession beginRestoreSessionForUser(
+ int userId, String packageName, String transportID) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
+ return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null;
}
@Override
public void opComplete(int token, long result) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.opComplete(token, result);
+ svc.opComplete(binderGetCallingUserId(), token, result);
}
}
@Override
- public long getAvailableRestoreToken(String packageName) {
+ public long getAvailableRestoreTokenForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
+ return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0;
}
@Override
- public boolean isAppEligibleForBackup(String packageName) {
+ public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
+ return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false;
}
@Override
- public String[] filterAppsEligibleForBackup(String[] packages) {
+ public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
+ return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null;
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 796ef406d333..2e414438c6ff 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -251,6 +251,7 @@ public class UserBackupManagerService {
private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+ private final @UserIdInt int mUserId;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -372,10 +373,11 @@ public class UserBackupManagerService {
* Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
* includes setting up the directories where we keep our bookkeeping and transport management.
*
- * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+ * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
* TransportManager)
*/
static UserBackupManagerService createAndInitializeService(
+ @UserIdInt int userId,
Context context,
Trampoline trampoline,
HandlerThread backupThread,
@@ -399,12 +401,13 @@ public class UserBackupManagerService {
File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
return createAndInitializeService(
- context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
/**
* Creates an instance of {@link UserBackupManagerService}.
*
+ * @param userId The user which this service is for.
* @param context The system server context.
* @param trampoline A reference to the proxy to {@link BackupManagerService}.
* @param backupThread The thread running backup/restore operations for the user.
@@ -415,6 +418,7 @@ public class UserBackupManagerService {
*/
@VisibleForTesting
public static UserBackupManagerService createAndInitializeService(
+ @UserIdInt int userId,
Context context,
Trampoline trampoline,
HandlerThread backupThread,
@@ -422,16 +426,18 @@ public class UserBackupManagerService {
File dataDir,
TransportManager transportManager) {
return new UserBackupManagerService(
- context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
private UserBackupManagerService(
+ @UserIdInt int userId,
Context context,
Trampoline parent,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
+ mUserId = userId;
mContext = checkNotNull(context, "context cannot be null");
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -2805,15 +2811,6 @@ public class UserBackupManagerService {
}
}
- /** Mark the backup service as having been provisioned. */
- public void setBackupProvisioned(boolean available) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupProvisioned");
- /*
- * This is now a no-op; provisioning is simply the device's own setup state.
- */
- }
-
/** Report whether the backup mechanism is currently enabled. */
public boolean isBackupEnabled() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -2863,19 +2860,6 @@ public class UserBackupManagerService {
return mTransportManager.getRegisteredTransportComponents();
}
- /** Report all system whitelisted transports. */
- public String[] getTransportWhitelist() {
- // No permission check, intentionally.
- Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
- String[] whitelistedTransports = new String[whitelistedComponents.size()];
- int i = 0;
- for (ComponentName component : whitelistedComponents) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
- }
-
/**
* Update the attributes of the transport identified by {@code transportComponent}. If the
* specified transport has not been bound at least once (for registration), this call will be
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 10e713d9dabe..e8820ae4e32a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -25,6 +25,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -34,7 +35,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
@@ -47,7 +47,6 @@ import com.android.server.infra.AbstractMasterSystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* A service used to observe the contents of the screen.
@@ -182,30 +181,18 @@ public final class ContentCaptureManagerService extends
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
- sessionId, clientContext, flags, mAllowInstantService, result);
+ sessionId, Binder.getCallingUid(), clientContext, flags,
+ mAllowInstantService, result);
}
}
@Override
- public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
+ public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
Preconditions.checkNotNull(sessionId);
- Preconditions.checkNotNull(events);
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
- service.sendEventsLocked(sessionId, events);
- }
- }
-
- @Override
- public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
- @Nullable List<ContentCaptureEvent> events) {
- Preconditions.checkNotNull(sessionId);
-
- synchronized (mLock) {
- final ContentCapturePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(sessionId, events);
+ service.finishSessionLocked(sessionId);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 81309122d0ca..f21b0d810411 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -16,6 +16,8 @@
package com.android.server.contentcapture;
+import static android.service.contentcapture.ContentCaptureService.setClientState;
+
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -38,7 +40,6 @@ import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
@@ -48,7 +49,6 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* Per-user instance of {@link ContentCaptureManagerService}.
@@ -114,10 +114,10 @@ final class ContentCapturePerUserService
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
- @NonNull String sessionId, @Nullable ContentCaptureContext clientContext,
- int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver resultReceiver) {
+ @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext,
+ int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) {
if (!isEnabledLocked()) {
- sendToClient(resultReceiver, ContentCaptureSession.STATE_DISABLED);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -131,35 +131,30 @@ final class ContentCapturePerUserService
return;
}
- ContentCaptureServerSession session = mSessions.get(sessionId);
- if (session != null) {
- if (mMaster.debug) {
- Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
- + componentName);
- }
- // TODO(b/111276913): check if local ids match and decide what to do if they don't
- // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
- // if not, move notifySessionStartedLocked() into session constructor
- sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+ final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
+ if (existingSession != null) {
+ Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ + ": ignoring because it already exists for " + existingSession.mActivityToken);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+ /* binder=*/ null);
return;
}
- session = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken,
- this, serviceComponentName, componentName, taskId, displayId, sessionId,
- clientContext, flags, bindInstantServiceAllowed, mMaster.verbose);
+ final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(),
+ mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId,
+ displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed,
+ mMaster.verbose);
if (mMaster.verbose) {
- Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
- + sessionId);
+ Slog.v(TAG, "startSession(): new session for "
+ + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
}
- mSessions.put(sessionId, session);
- session.notifySessionStartedLocked();
- sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+ mSessions.put(sessionId, newSession);
+ newSession.notifySessionStartedLocked(clientReceiver);
}
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
- public void finishSessionLocked(@NonNull String sessionId,
- @Nullable List<ContentCaptureEvent> events) {
+ public void finishSessionLocked(@NonNull String sessionId) {
if (!isEnabledLocked()) {
return;
}
@@ -171,41 +166,8 @@ final class ContentCapturePerUserService
}
return;
}
- if (events != null && !events.isEmpty()) {
- // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
- // calls because it's not clear yet whether we'll change the manager to send events
- // to the service directly (i.e., without passing through system server). Once we
- // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
- // methods, one for start and another for finish (and passing the events to finish),
- // otherwise the service might receive the 2 calls out of order.
- session.sendEventsLocked(events);
- }
- if (mMaster.verbose) {
- Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
- + session);
- }
- session.removeSelfLocked(true);
- }
-
- // TODO(b/111276913): need to figure out why some events are sent before session is started;
- // probably because ContentCaptureManager is not buffering them until it gets the session back
- @GuardedBy("mLock")
- public void sendEventsLocked(@NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- if (!isEnabledLocked()) {
- return;
- }
- final ContentCaptureServerSession session = mSessions.get(sessionId);
- if (session == null) {
- if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): no session for " + sessionId);
- }
- return;
- }
- if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
- }
- session.sendEventsLocked(events);
+ if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
+ session.removeSelfLocked(/* notifyRemoteService= */ true);
}
@GuardedBy("mLock")
@@ -308,12 +270,4 @@ final class ContentCapturePerUserService
}
return null;
}
-
- private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
- try {
- resultReceiver.send(value, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error async reporting result to client: " + e);
- }
- }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 181a2daa2467..ba98b95082f7 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -24,15 +24,14 @@ import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureSessionId;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import java.io.PrintWriter;
-import java.util.List;
final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks {
@@ -43,18 +42,28 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
private final ContentCapturePerUserService mService;
private final RemoteContentCaptureService mRemoteService;
private final ContentCaptureContext mContentCaptureContext;
+
+ /**
+ * Canonical session id.
+ */
private final String mId;
+ /**
+ * UID of the app whose contents is being captured.
+ */
+ private final int mUid;
+
ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock,
@NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
@NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, @NonNull String sessionId,
+ int taskId, int displayId, @NonNull String sessionId, int uid,
@Nullable ContentCaptureContext clientContext, int flags,
boolean bindInstantServiceAllowed, boolean verbose) {
mLock = lock;
mActivityToken = activityToken;
mService = service;
mId = Preconditions.checkNotNull(sessionId);
+ mUid = uid;
mRemoteService = new RemoteContentCaptureService(context,
ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
bindInstantServiceAllowed, verbose);
@@ -73,15 +82,8 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
* Notifies the {@link ContentCaptureService} that the service started.
*/
@GuardedBy("mLock")
- public void notifySessionStartedLocked() {
- mRemoteService.onSessionLifecycleRequest(mContentCaptureContext, mId);
- }
-
- /**
- * Notifies the {@link ContentCaptureService} of a batch of events.
- */
- public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
- mRemoteService.onContentCaptureEventsRequest(mId, events);
+ public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
+ mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver);
}
/**
@@ -118,11 +120,11 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
@GuardedBy("mLock")
public void destroyLocked(boolean notifyRemoteService) {
if (mService.isVerbose()) {
- Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+ Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
}
// TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
if (notifyRemoteService) {
- mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+ mRemoteService.onSessionFinished(mId);
}
}
@@ -133,13 +135,14 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
Slog.d(TAG, "onServiceDied() for " + mId);
}
synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
+ removeSelfLocked(/* notifyRemoteService= */ true);
}
}
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
+ pw.print(prefix); pw.print("uid: "); pw.print(mUid); pw.println();
pw.print(prefix); pw.print("context: "); mContentCaptureContext.dump(pw); pw.println();
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
pw.print(prefix); pw.print("has autofill callback: ");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index b4edf7e60bc4..942ee11aa481 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,16 +20,13 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
-import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
-
-import java.util.List;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.os.IResultReceiver;
final class RemoteContentCaptureService
extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
@@ -67,21 +64,19 @@ final class RemoteContentCaptureService
/**
* Called by {@link ContentCaptureServerSession} to generate a call to the
- * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
- * is not {@code null} or destroyed (when {@code context} is {@code null}).
+ * {@link RemoteContentCaptureService} to indicate the session was created.
*/
- public void onSessionLifecycleRequest(@Nullable ContentCaptureContext context,
- @NonNull String sessionId) {
- scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
+ public void onSessionStarted(@Nullable ContentCaptureContext context,
+ @NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) {
+ scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver));
}
/**
- * Called by {@link ContentCaptureServerSession} to send a batch of events to the service.
+ * Called by {@link ContentCaptureServerSession} to generate a call to the
+ * {@link RemoteContentCaptureService} to indicate the session was finished.
*/
- public void onContentCaptureEventsRequest(@NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
- new ContentCaptureEventsRequest(events)));
+ public void onSessionFinished(@NonNull String sessionId) {
+ scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
}
/**
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 08034f734bea..0fa996ed7657 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1123,8 +1123,6 @@ class AlarmManagerService extends SystemService {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
- // And send a TIME_TICK right now, since it is important to get the UI updated.
- mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
}
static final class InFlight {
@@ -1298,7 +1296,7 @@ class AlarmManagerService extends SystemService {
mInjector.init();
synchronized (mLock) {
- mHandler = new AlarmHandler(Looper.myLooper());
+ mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
mNextWakeup = mNextNonWakeup = 0;
@@ -3050,6 +3048,9 @@ class AlarmManagerService extends SystemService {
mNonInteractiveTime = dur;
}
}
+ // And send a TIME_TICK right now, since it is important to get the UI updated.
+ mHandler.post(() ->
+ getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
} else {
mNonInteractiveStartTime = nowELAPSED;
}
@@ -3838,7 +3839,8 @@ class AlarmManagerService extends SystemService {
mWakeLock.setWorkSource(null);
}
- private class AlarmHandler extends Handler {
+ @VisibleForTesting
+ class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
public static final int LISTENER_TIMEOUT = 3;
@@ -3847,8 +3849,8 @@ class AlarmManagerService extends SystemService {
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
- AlarmHandler(Looper looper) {
- super(looper);
+ AlarmHandler() {
+ super(Looper.myLooper());
}
public void postRemoveForStopped(int uid) {
@@ -3961,8 +3963,8 @@ class AlarmManagerService extends SystemService {
final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
- 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
- null, Process.myUid(), "android");
+ 0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE,
+ workSource, null, Process.myUid(), "android");
// Finally, remember when we set the tick alarm
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 8d912fadf6d1..f027253651ee 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -65,7 +65,6 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -86,6 +85,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
@@ -456,6 +456,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
+ final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
final class ModeCallback implements DeathRecipient {
@@ -475,6 +476,7 @@ public class AppOpsService extends IAppOpsService.Stub {
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
+ /*ignored*/
}
}
@@ -524,6 +526,7 @@ public class AppOpsService extends IAppOpsService.Stub {
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
+ /*ignored*/
}
}
@@ -552,6 +555,50 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ final class NotedCallback implements DeathRecipient {
+ final IAppOpsNotedCallback mCallback;
+ final int mWatchingUid;
+ final int mCallingUid;
+ final int mCallingPid;
+
+ NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
+ int callingPid) {
+ mCallback = callback;
+ mWatchingUid = watchingUid;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("NotedCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, mWatchingUid);
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append(" pid=");
+ sb.append(mCallingPid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void destroy() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingNoted(mCallback);
+ }
+ }
+
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
final class ClientState extends Binder implements DeathRecipient {
@@ -1598,29 +1645,40 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
+ public int checkOperationRaw(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, true /*raw*/);
+ }
+
+ @Override
public int checkOperation(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, false /*raw*/);
+ }
+
+ private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
final CheckOpsDelegate delegate;
synchronized (this) {
delegate = mCheckOpsDelegate;
}
if (delegate == null) {
- return checkOperationImpl(code, uid, packageName);
+ return checkOperationImpl(code, uid, packageName, raw);
}
- return delegate.checkOperation(code, uid, packageName,
+ return delegate.checkOperation(code, uid, packageName, raw,
AppOpsService.this::checkOperationImpl);
}
- private int checkOperationImpl(int code, int uid, String packageName) {
+ private int checkOperationImpl(int code, int uid, String packageName,
+ boolean raw) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return checkOperationUnchecked(code, uid, resolvedPackageName);
+ return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
}
- private int checkOperationUnchecked(int code, int uid, String packageName) {
+ private int checkOperationUnchecked(int code, int uid, String packageName,
+ boolean raw) {
synchronized (this) {
if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
@@ -1629,7 +1687,8 @@ public class AppOpsService extends IAppOpsService.Stub {
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
- return uidState.opModes.get(code);
+ final int rawMode = uidState.opModes.get(code);
+ return raw ? rawMode : uidState.evalMode(rawMode);
}
Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
@@ -1795,12 +1854,16 @@ public class AppOpsService extends IAppOpsService.Stub {
final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
false /* uidMismatchExpected */);
if (ops == null) {
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, packageName)) {
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
}
final UidState uidState = ops.uidState;
@@ -1820,6 +1883,7 @@ public class AppOpsService extends IAppOpsService.Stub {
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
return uidMode;
}
} else {
@@ -1830,6 +1894,7 @@ public class AppOpsService extends IAppOpsService.Stub {
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
+ scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
return mode;
}
}
@@ -1839,6 +1904,8 @@ public class AppOpsService extends IAppOpsService.Stub {
op.rejectTime[uidState.state] = 0;
op.proxyUid = proxyUid;
op.proxyPackageName = proxyPackageName;
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_ALLOWED);
return AppOpsManager.MODE_ALLOWED;
}
}
@@ -1886,10 +1953,50 @@ public class AppOpsService extends IAppOpsService.Stub {
}
final int callbackCount = activeCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- // Apps ops are mapped to a singleton
- if (i == 0) {
- activeCallbacks.valueAt(i).destroy();
- }
+ activeCallbacks.valueAt(i).destroy();
+ }
+ }
+ }
+
+ @Override
+ public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
+ int watchedUid = Process.INVALID_UID;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = callingUid;
+ }
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+ Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+ "Invalid op code in: " + Arrays.toString(ops));
+ Preconditions.checkNotNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
+ if (callbacks == null) {
+ callbacks = new SparseArray<>();
+ mNotedWatchers.put(callback.asBinder(), callbacks);
+ }
+ final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
+ callingUid, callingPid);
+ for (int op : ops) {
+ callbacks.put(op, notedCallback);
+ }
+ }
+ }
+
+ @Override
+ public void stopWatchingNoted(IAppOpsNotedCallback callback) {
+ Preconditions.checkNotNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ final SparseArray<NotedCallback> notedCallbacks =
+ mNotedWatchers.remove(callback.asBinder());
+ if (notedCallbacks == null) {
+ return;
+ }
+ final int callbackCount = notedCallbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ notedCallbacks.valueAt(i).destroy();
}
}
}
@@ -2052,6 +2159,51 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
+ int result) {
+ ArraySet<NotedCallback> dispatchedCallbacks = null;
+ final int callbackListCount = mNotedWatchers.size();
+ for (int i = 0; i < callbackListCount; i++) {
+ final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
+ final NotedCallback callback = callbacks.get(code);
+ if (callback != null) {
+ if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+ continue;
+ }
+ if (dispatchedCallbacks == null) {
+ dispatchedCallbacks = new ArraySet<>();
+ }
+ dispatchedCallbacks.add(callback);
+ }
+ }
+ if (dispatchedCallbacks == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChecked,
+ this, dispatchedCallbacks, code, uid, packageName, result));
+ }
+
+ private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
+ int code, int uid, String packageName, int result) {
+ // There are components watching for checks in our process. The callbacks in
+ // these components may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final NotedCallback callback = callbacks.valueAt(i);
+ try {
+ callback.mCallback.opNoted(code, uid, packageName, result);
+ } catch (RemoteException e) {
+ /* do nothing */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public int permissionToOpCode(String permission) {
if (permission == null) {
@@ -3463,6 +3615,46 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println(cb);
}
}
+ if (mNotedWatchers.size() > 0 && dumpMode < 0) {
+ needSep = true;
+ boolean printedHeader = false;
+ for (int i = 0; i < mNotedWatchers.size(); i++) {
+ final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i);
+ if (notedWatchers.size() <= 0) {
+ continue;
+ }
+ final NotedCallback cb = notedWatchers.valueAt(0);
+ if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+ if (dumpPackage != null && cb.mWatchingUid >= 0
+ && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" All op noted watchers:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(
+ mNotedWatchers.keyAt(i))));
+ pw.println(" ->");
+ pw.print(" [");
+ final int opCount = notedWatchers.size();
+ for (i = 0; i < opCount; i++) {
+ if (i > 0) {
+ pw.print(' ');
+ }
+ pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i)));
+ if (i < opCount - 1) {
+ pw.print(',');
+ }
+ }
+ pw.println("]");
+ pw.print(" ");
+ pw.println(cb);
+ }
+ }
if (mClients.size() > 0 && dumpMode < 0) {
needSep = true;
boolean printedHeader = false;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a07939e86325..7bbc543a6aab 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -76,6 +76,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -1276,7 +1277,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mService == null) {
return;
}
- mService.unlinkToDeath(this, 0);
+ try {
+ mService.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Log.e(TAG, "error unlinking to death", e);
+ }
mService = null;
mClassName = null;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 89194e43bf95..66ceae432c6e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5714,7 +5714,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
mNMS.createVirtualNetwork(networkAgent.network.netId,
- !networkAgent.linkProperties.getDnsServers().isEmpty(),
(networkAgent.networkMisc == null ||
!networkAgent.networkMisc.allowBypass));
} else {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 4e8ef54a6465..d33b61757141 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -302,12 +302,14 @@ public class LocationManagerService extends ILocationManager.Stub {
AppOpsManager.OnOpChangedListener callback
= new AppOpsManager.OnOpChangedInternalListener() {
public void onOpChanged(int op, String packageName) {
- synchronized (mLock) {
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- applyAllProviderRequirementsLocked();
- }
+ mLocationHandler.post(() -> {
+ synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ receiver.updateMonitoring(true);
+ }
+ applyAllProviderRequirementsLocked();
+ }
+ });
}
};
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8869af4e334a..b0ca2df20f1f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2314,11 +2314,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) {
+ public void createVirtualNetwork(int netId, boolean secure) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mNetdService.networkCreateVpn(netId, hasDNS, secure);
+ mNetdService.networkCreateVpn(netId, secure);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6c62725bb5bd..7adcabac26ee 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1504,6 +1504,10 @@ class StorageManagerService extends IStorageManager.Stub
public StorageManagerService(Context context) {
sSelf = this;
+ // Snapshot feature flag used for this boot
+ SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
+ SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
+
mContext = context;
mCallbacks = new Callbacks(FgThread.get().getLooper());
mLockPatternUtils = new LockPatternUtils(mContext);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1ef3e942ca23..d07cf78e47ed 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -214,6 +214,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private PreciseCallState mPreciseCallState = new PreciseCallState();
+ private int mCallDisconnectCause = DisconnectCause.NOT_VALID;
+
+ private int mCallPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
private boolean mCarrierNetworkChangeState = false;
private PhoneCapability mPhoneCapability = null;
@@ -714,6 +718,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+ try {
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+ mCallPreciseDisconnectCause);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onPreciseDataConnectionStateChanged(
@@ -1491,9 +1503,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
handleRemoveListLocked();
}
- broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
- DisconnectCause.NOT_VALID,
- PreciseDisconnectCause.NOT_VALID);
+ broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
+ backgroundCallState);
}
public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
@@ -1501,12 +1512,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return;
}
synchronized (mRecords) {
- mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState,
- mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+ mCallDisconnectCause = disconnectCause;
+ mCallPreciseDisconnectCause = preciseDisconnectCause;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener
+ .LISTEN_CALL_DISCONNECT_CAUSES)) {
try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+ mCallPreciseDisconnectCause);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1514,8 +1527,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
handleRemoveListLocked();
}
- broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
- mBackgroundCallState, disconnectCause, preciseDisconnectCause);
}
public void notifyPreciseDataConnectionFailed(String reason, String apnType,
@@ -1737,6 +1748,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
pw.println("mPreciseCallState=" + mPreciseCallState);
+ pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
+ pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause);
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
pw.println("mRingingCallState=" + mRingingCallState);
pw.println("mForegroundCallState=" + mForegroundCallState);
@@ -1912,13 +1925,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
- int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+ int backgroundCallState) {
Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
- intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
- intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
}
@@ -2006,6 +2017,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
}
+ if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5afb90d681af..a94fa12e4f35 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1665,7 +1665,7 @@ public final class ActiveServices {
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
- callerApp.uid, callerApp.processName);
+ callerApp.uid, callerApp.processName, callingPackage);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
@@ -2023,7 +2023,7 @@ public final class ActiveServices {
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
name.getPackageName(), sInfo.applicationInfo.uid)) {
String msg = "association not allowed between packages "
- + callingPackage + " and " + r.packageName;
+ + callingPackage + " and " + name.getPackageName();
Slog.w(TAG, "Service lookup failed: " + msg);
return new ServiceLookupResult(null, msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 739dbbc65f6f..983ec4be328d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.REMOVE_TASKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -2329,9 +2330,16 @@ public class ActivityManagerService extends IActivityManager.Stub
fos.close();
long[] rssAfter = Process.getRss(pid);
long end = SystemClock.uptimeMillis();
+ long time = end - start;
EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], end-start);
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+ StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1,
+ ActivityManager.processStateAmToProto(msg.arg2));
synchronized(ActivityManagerService.this) {
proc.lastCompactTime = end;
proc.lastCompactAction = pendingAction;
@@ -6284,7 +6292,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
- String callingTag, boolean stable) {
+ String callingPackage, String callingTag, boolean stable) {
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
@@ -6304,7 +6312,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return conn;
}
}
- ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+ ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
conn.startAssociationIfNeeded();
if (stable) {
conn.stableCount = 1;
@@ -6411,8 +6419,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, int callingUid, String callingTag, boolean stable,
- int userId) {
+ String name, IBinder token, int callingUid, String callingPackage, String callingTag,
+ boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -6535,7 +6543,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// In this case the provider instance already exists, so we can
// return it right away.
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
@@ -6782,7 +6791,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mProviderMap.putProviderByName(name, cpr);
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable);
if (conn != null) {
conn.waiting = true;
}
@@ -6924,7 +6934,8 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public final ContentProviderHolder getContentProvider(
- IApplicationThread caller, String name, int userId, boolean stable) {
+ IApplicationThread caller, String callingPackage, String name, int userId,
+ boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
@@ -6934,8 +6945,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
- return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
- userId);
+ final int callingUid = Binder.getCallingUid();
+ if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException("Given calling package " + callingPackage
+ + " does not match caller's uid " + callingUid);
+ }
+ return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
+ null, stable, userId);
}
public ContentProviderHolder getContentProviderExternal(
@@ -6950,7 +6967,8 @@ public class ActivityManagerService extends IActivityManager.Stub
private ContentProviderHolder getContentProviderExternalUnchecked(String name,
IBinder token, int callingUid, String callingTag, int userId) {
- return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
+ return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+ true, userId);
}
/**
@@ -15694,7 +15712,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
- boolean doingAll, long now) {
+ boolean doingAll, long now, boolean cycleReEval) {
if (mAdjSeq == app.adjSeq) {
if (app.adjSeq == app.completedAdjSeq) {
// This adjustment has already been computed successfully.
@@ -15760,20 +15778,21 @@ public class ActivityManagerService extends IActivityManager.Stub
app.systemNoUi = false;
}
if (!app.systemNoUi) {
- if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
- // screen on, promote UI
- app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
- } else {
- // screen off, restrict UI scheduling
- app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
- }
+ if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ // screen on, promote UI
+ app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ } else {
+ // screen off, restrict UI scheduling
+ app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+ }
}
+ app.setCurRawProcState(app.getCurProcState());
app.curAdj = app.maxAdj;
app.completedAdjSeq = app.adjSeq;
// if curAdj is less than prevAppAdj, then this process was promoted
- return app.curAdj < prevAppAdj;
+ return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
}
app.systemNoUi = false;
@@ -16015,8 +16034,13 @@ public class ActivityManagerService extends IActivityManager.Stub
// By default, we use the computed adjustment. It may be changed if
// there are applications dependent on our services or providers, but
// this gives us a baseline and makes sure we don't get into an
- // infinite recursion.
- app.setCurRawAdj(adj);
+ // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
+ // values.
+ app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+ app.setCurRawProcState(!cycleReEval
+ ? procState
+ : Math.min(procState, app.getCurRawProcState()));
+
app.hasStartedServices = false;
app.adjSeq = mAdjSeq;
@@ -16118,21 +16142,15 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean trackedProcState = false;
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
- if (client.containsCycle) {
- // We've detected a cycle. We should retry computeOomAdjLocked later in
- // case a later-checked connection from a client would raise its
- // priority legitimately.
- app.containsCycle = true;
- // If the client has not been completely evaluated, skip using its
- // priority. Else use the conservative value for now and look for a
- // better state in the next iteration.
- if (client.completedAdjSeq < mAdjSeq) {
- continue;
- }
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+ if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ continue;
}
+
int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurProcState();
+ int clientProcState = client.getCurRawProcState();
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
@@ -16217,6 +16235,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (adj > newAdj) {
adj = newAdj;
+ app.setCurRawAdj(adj);
adjType = "service";
}
}
@@ -16288,6 +16307,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (procState > clientProcState) {
procState = clientProcState;
+ app.setCurRawProcState(procState);
if (adjType == null) {
adjType = "service";
}
@@ -16319,6 +16339,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
&& a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -16360,21 +16381,15 @@ public class ActivityManagerService extends IActivityManager.Stub
// Being our own client is not interesting.
continue;
}
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
- if (client.containsCycle) {
- // We've detected a cycle. We should retry computeOomAdjLocked later in
- // case a later-checked connection from a client would raise its
- // priority legitimately.
- app.containsCycle = true;
- // If the client has not been completely evaluated, skip using its
- // priority. Else use the conservative value for now and look for a
- // better state in the next iteration.
- if (client.completedAdjSeq < mAdjSeq) {
- continue;
- }
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+ if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ continue;
}
+
int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurProcState();
+ int clientProcState = client.getCurRawProcState();
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
@@ -16388,6 +16403,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
adjType = "provider";
}
app.cached &= client.cached;
@@ -16423,6 +16439,7 @@ public class ActivityManagerService extends IActivityManager.Stub
conn.trackProcState(clientProcState, mAdjSeq, now);
if (procState > clientProcState) {
procState = clientProcState;
+ app.setCurRawProcState(procState);
}
if (client.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -16448,6 +16465,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.adjType = "ext-provider";
@@ -16459,6 +16477,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ app.setCurRawProcState(procState);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to external provider: " + app);
@@ -16603,6 +16622,7 @@ public class ActivityManagerService extends IActivityManager.Stub
app.curAdj = app.modifyRawOomAdj(adj);
app.setCurrentSchedulingGroup(schedGroup);
app.setCurProcState(procState);
+ app.setCurRawProcState(procState);
app.setHasForegroundActivities(foregroundActivities);
app.completedAdjSeq = mAdjSeq;
@@ -16610,6 +16630,44 @@ public class ActivityManagerService extends IActivityManager.Stub
return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
}
+ /**
+ * Checks if for the given app and client, there's a cycle that should skip over the client
+ * for now or use partial values to evaluate the effect of the client binding.
+ * @param app
+ * @param client
+ * @param procState procstate evaluated so far for this app
+ * @param adj oom_adj evaluated so far for this app
+ * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+ * evaluation.
+ * @return whether to skip using the client connection at this time
+ */
+ private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+ int procState, int adj, boolean cycleReEval) {
+ if (client.containsCycle) {
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
+ app.containsCycle = true;
+ // If the client has not been completely evaluated, check if it's worth
+ // using the partial values.
+ if (client.completedAdjSeq < mAdjSeq) {
+ if (cycleReEval) {
+ // If the partial values are no better, skip until the next
+ // attempt
+ if (client.getCurRawProcState() >= procState
+ && client.getCurRawAdj() >= adj) {
+ return true;
+ }
+ // Else use the client's partial procstate and adj to adjust the
+ // effect of the binding
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private static final class RecordPssRunnable implements Runnable {
private final ActivityManagerService mService;
private final ProcessRecord mProc;
@@ -17028,12 +17086,16 @@ public class ActivityManagerService extends IActivityManager.Stub
app.curAdj == ProcessList.HOME_APP_ADJ)) {
app.reqCompactAction = COMPACT_PROCESS_SOME;
mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
} else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
app.reqCompactAction = COMPACT_PROCESS_FULL;
mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
}
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
@@ -17472,7 +17534,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
- computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+ computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
@@ -17847,12 +17909,14 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
app.containsCycle = false;
+ app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+ app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
}
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
- computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+ computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
// if any app encountered a cycle, we need to perform an additional loop later
retryCycles |= app.containsCycle;
@@ -17955,8 +18019,8 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i=0; i<N; i++) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-
- if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+ if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+ true)) {
retryCycles = true;
}
}
@@ -20053,18 +20117,18 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl) {
+ public int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, Process.SHELL_UID,
- "com.android.shell");
+ "com.android.shell", raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName);
+ return superImpl.apply(code, uid, packageName, raw);
}
@Override
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index aa76b3d6b856..af1031e5b887 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -43,6 +43,7 @@ final class ConnectionRecord {
final PendingIntent clientIntent; // How to launch the client.
final int clientUid; // The identity of this connection's client
final String clientProcessName; // The source process of this connection's client
+ final String clientPackageName; // The source package of this connection's client
public AssociationState.SourceState association; // Association tracking
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
@@ -96,7 +97,7 @@ final class ConnectionRecord {
ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent,
- int _clientUid, String _clientProcessName) {
+ int _clientUid, String _clientProcessName, String _clientPackageName) {
binding = _binding;
activity = _activity;
conn = _conn;
@@ -105,6 +106,7 @@ final class ConnectionRecord {
clientIntent = _clientIntent;
clientUid = _clientUid;
clientProcessName = _clientProcessName;
+ clientPackageName = _clientPackageName;
}
public void startAssociationIfNeeded() {
@@ -125,7 +127,7 @@ final class ConnectionRecord {
} else {
association = holder.pkg.getAssociationStateLocked(holder.state,
binding.service.instanceName.getClassName()).startSource(clientUid,
- clientProcessName);
+ clientProcessName, clientPackageName);
}
}
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2d4f739c8bf..5f184c2dabee 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -32,6 +32,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
public final class ContentProviderConnection extends Binder {
public final ContentProviderRecord provider;
public final ProcessRecord client;
+ public final String clientPackage;
public AssociationState.SourceState association;
public final long createTime;
public int stableCount;
@@ -46,9 +47,11 @@ public final class ContentProviderConnection extends Binder {
public int numStableIncs;
public int numUnstableIncs;
- public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
+ public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client,
+ String _clientPackage) {
provider = _provider;
client = _client;
+ clientPackage = _clientPackage;
createTime = SystemClock.elapsedRealtime();
}
@@ -69,7 +72,8 @@ public final class ContentProviderConnection extends Binder {
+ provider.name.toShortString() + ": proc=" + provider.proc);
} else {
association = holder.pkg.getAssociationStateLocked(holder.state,
- provider.name.getClassName()).startSource(client.uid, client.processName);
+ provider.name.getClassName()).startSource(client.uid, client.processName,
+ clientPackage);
}
}
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 2fc4adc2dc6c..46dfc7c60fd9 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -305,7 +305,7 @@ final class ContentProviderRecord implements ComponentName.WithComponentName {
} else {
mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
provider.name.getClassName()).startSource(mOwningUid,
- mOwningProcessName);
+ mOwningProcessName, null);
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index fa7a4c532f42..a71f6af80bec 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -138,4 +138,4 @@ option java_package com.android.server.am
30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
# The task is being compacted
-30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3) \ No newline at end of file
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2)
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5208ca5eb0db..e483b2640c00 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -15,7 +15,7 @@ ogunwale@google.com
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c4b715042953..c15b7c7d82c2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -153,6 +153,7 @@ final class ProcessRecord implements WindowProcessListener {
int trimMemoryLevel; // Last selected memory trimming level
private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+ private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
int pssStatType; // The type of stat collection that we are currently requesting
@@ -902,6 +903,7 @@ final class ProcessRecord implements WindowProcessListener {
if (mRepProcState > newState) {
mRepProcState = newState;
setCurProcState(newState);
+ setCurRawProcState(newState);
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
uid, processName, pkgList.keyAt(ipkg),
@@ -984,6 +986,14 @@ final class ProcessRecord implements WindowProcessListener {
return mCurProcState;
}
+ void setCurRawProcState(int curRawProcState) {
+ mCurRawProcState = curRawProcState;
+ }
+
+ int getCurRawProcState() {
+ return mCurRawProcState;
+ }
+
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4c4a09060706..d5ede5b63edc 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -19,8 +19,10 @@ package com.android.server.am;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -61,13 +63,18 @@ class SettingsToPropertiesMapper {
// Add the global setting you want to push to native level as experiment flag into this list.
//
// NOTE: please grant write permission system property prefix
- // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
- // permission in the corresponding .te file your feature belongs to.
+ // with format persist.device_config.global_settings.[flag_name] in system_server.te and grant
+ // read permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sGlobalSettings = new String[] {
Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
};
+ // All the flags under the listed DeviceConfig scopes will be synced to native level.
+ //
+ // NOTE: please grant write permission system property prefix
+ // with format persist.device_config.[device_config_scope]. in system_server.te and grant read
+ // permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sDeviceConfigScopes = new String[] {
};
@@ -104,19 +111,31 @@ class SettingsToPropertiesMapper {
ContentObserver co = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
- updatePropertyFromSetting(globalSetting, propName, true);
+ updatePropertyFromSetting(globalSetting, propName);
}
};
// only updating on starting up when no native flags reset is performed during current
// booting.
if (!isNativeFlagsResetPerformed()) {
- updatePropertyFromSetting(globalSetting, propName, true);
+ updatePropertyFromSetting(globalSetting, propName);
}
mContentResolver.registerContentObserver(settingUri, false, co);
}
- // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+ for (String deviceConfigScope : mDeviceConfigScopes) {
+ DeviceConfig.addOnPropertyChangedListener(
+ deviceConfigScope,
+ AsyncTask.THREAD_POOL_EXECUTOR,
+ (String scope, String name, String value) -> {
+ String propertyName = makePropertyName(scope, name);
+ if (propertyName == null) {
+ log("unable to construct system property for " + scope + "/" + name);
+ return;
+ }
+ setProperty(propertyName, value);
+ });
+ }
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -184,15 +203,6 @@ class SettingsToPropertiesMapper {
return propertyName;
}
- private String getSetting(String name, boolean isGlobalSetting) {
- if (isGlobalSetting) {
- return Settings.Global.getString(mContentResolver, name);
- } else {
- // TODO: complete the code after DeviceConfig APIs implemented.
- return null;
- }
- }
-
private void setProperty(String key, String value) {
// Check if need to clear the property
if (value == null) {
@@ -259,8 +269,8 @@ class SettingsToPropertiesMapper {
}
@VisibleForTesting
- void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
- String settingValue = getSetting(settingName, isGlobalSetting);
+ void updatePropertyFromSetting(String settingName, String propName) {
+ String settingValue = Settings.Global.getString(mContentResolver, settingName);
setProperty(propName, settingValue);
}
}
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2feea41807f7..905f82693980 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -20,11 +20,11 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioSystem;
import android.media.IRecordingConfigDispatcher;
import android.media.MediaRecorder;
+import android.media.audiofx.AudioEffect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -64,12 +64,19 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
* Implementation of android.media.AudioSystem.AudioRecordingCallback
*/
public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingInfo, String packName) {
+ int portId, boolean silenced, int[] recordingInfo,
+ AudioEffect.Descriptor[] clientEffects,
+ AudioEffect.Descriptor[] effects,
+ int activeSource, String packName) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
final List<AudioRecordingConfiguration> configsSystem =
- updateSnapshot(event, uid, session, source, recordingInfo);
+ updateSnapshot(event, uid, session, source, recordingInfo,
+ portId, silenced, activeSource, clientEffects, effects);
if (configsSystem != null){
synchronized (mClients) {
// list of recording configurations for "public consumption". It is only computed if
@@ -179,13 +186,20 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
+ * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\
+ int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the definition of the contents of the array
+ * @param portId
+ * @param silenced
+ * @param activeSource
+ * @param clientEffects
+ * @param effects
* @return null if the list of active recording sessions has not been modified, a list
* with the current active configurations otherwise.
*/
private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
- int source, int[] recordingInfo) {
+ int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
final boolean configChanged;
final ArrayList<AudioRecordingConfiguration> configs;
synchronized(mRecordConfigs) {
@@ -211,7 +225,7 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
.setSampleRate(recordingInfo[5])
.build();
final int patchHandle = recordingInfo[6];
- final Integer sessionKey = new Integer(session);
+ final Integer portIdKey = new Integer(portId);
final String[] packages = mPackMan.getPackagesForUid(uid);
final String packageName;
@@ -222,19 +236,20 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
}
final AudioRecordingConfiguration updatedConfig =
new AudioRecordingConfiguration(uid, session, source,
- clientFormat, deviceFormat, patchHandle, packageName);
+ clientFormat, deviceFormat, patchHandle, packageName,
+ portId, silenced, activeSource, clientEffects, effects);
- if (mRecordConfigs.containsKey(sessionKey)) {
- if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+ if (mRecordConfigs.containsKey(portIdKey)) {
+ if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
configChanged = false;
} else {
// config exists but has been modified
- mRecordConfigs.remove(sessionKey);
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.remove(portIdKey);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
} else {
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
if (configChanged) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 1882be26ebf0..a381477b01cb 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -653,7 +653,7 @@ public class BiometricService extends SystemService {
}
mHandler.post(() -> {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
final int modality = result.first;
final int error = result.second;
@@ -950,7 +950,7 @@ public class BiometricService extends SystemService {
* {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
- private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
+ private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
int modality = TYPE_NONE;
// No biometric features, send error
@@ -979,7 +979,7 @@ public class BiometricService extends SystemService {
// order.
firstHwAvailable = modality;
}
- if (authenticator.hasEnrolledTemplates(callingUid)) {
+ if (authenticator.hasEnrolledTemplates(userId)) {
hasTemplatesEnrolled = true;
if (isEnabledForApp(modality)) {
// TODO(b/110907543): When face settings (and other settings) have both a
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 422f5566eee1..e40949b785d9 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -217,6 +217,19 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
@Override
// Called concurrently by multiple binder threads.
// This method must not block or perform long-running operations.
+ public synchronized void onNat64PrefixEvent(int netId,
+ boolean added, String prefixString, int prefixLength)
+ throws RemoteException {
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength);
+ }
+ }
+ }
+
+ @Override
+ // Called concurrently by multiple binder threads.
+ // This method must not block or perform long-running operations.
public synchronized void onPrivateDnsValidationEvent(int netId,
String ipAddress, String hostname, boolean validated)
throws RemoteException {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15500ea..52ecccaa7367 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,13 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -27,6 +34,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
@@ -34,14 +43,15 @@ import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
class AutomaticBrightnessController {
private static final String TAG = "AutomaticBrightnessController";
- private static final boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, enables the use of the screen auto-brightness adjustment setting.
@@ -66,6 +76,8 @@ class AutomaticBrightnessController {
private static final int MSG_UPDATE_AMBIENT_LUX = 1;
private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+ private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+ private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -126,6 +138,8 @@ class AutomaticBrightnessController {
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private boolean mLoggingEnabled;
+
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
// May be 0 if no warm-up is required.
@@ -192,6 +206,19 @@ class AutomaticBrightnessController {
private float mShortTermModelAnchor;
private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
+ // Context-sensitive brightness configurations require keeping track of the foreground app's
+ // package name and category, which is done by registering a TaskStackListener to call back to
+ // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+ // package namd and PackageManager to get its category (so might as well cache them).
+ private int mUserId;
+ private String mForegroundAppPackageName;
+ private String mPendingForegroundAppPackageName;
+ private @ApplicationInfo.Category int mForegroundAppCategory;
+ private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+ private TaskStackListenerImpl mTaskStackListener;
+ private IActivityTaskManager mActivityTaskManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, BrightnessMappingStrategy mapper,
int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -226,6 +253,42 @@ class AutomaticBrightnessController {
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
+
+ mUserId = ActivityManager.getCurrentUser();
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mTaskStackListener = new TaskStackListenerImpl();
+ mForegroundAppPackageName = null;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Update the current user's ID.
+ *
+ * @param userId
+ * The current user's ID.
+ */
+ public void onSwitchUser(int userId) {
+ mUserId = userId;
}
public int getAutomaticScreenBrightness() {
@@ -290,7 +353,7 @@ class AutomaticBrightnessController {
}
final int oldPolicy = mDisplayPolicy;
mDisplayPolicy = policy;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -317,7 +380,7 @@ class AutomaticBrightnessController {
mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
}
return true;
@@ -330,7 +393,7 @@ class AutomaticBrightnessController {
}
private void invalidateShortTermModel() {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: invalidate user data");
}
mShortTermModelValid = false;
@@ -383,7 +446,11 @@ class AutomaticBrightnessController {
pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mShortTermModelValid=" + mShortTermModelValid);
+ pw.println(" mUserId=" + mUserId);
+ pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
+ pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
+ pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
pw.println();
mBrightnessMapper.dump(pw);
@@ -399,6 +466,7 @@ class AutomaticBrightnessController {
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mCurrentLightSensorRate = mInitialLightSensorRate;
+ registerForegroundAppUpdater();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
mCurrentLightSensorRate * 1000, mHandler);
return true;
@@ -411,6 +479,7 @@ class AutomaticBrightnessController {
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ unregisterForegroundAppUpdater();
mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
@@ -441,7 +510,7 @@ class AutomaticBrightnessController {
private void adjustLightSensorRate(int lightSensorRate) {
// if the light sensor rate changed, update the sensor listener
if (lightSensorRate != mCurrentLightSensorRate) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "adjustLightSensorRate: " +
"previousRate=" + mCurrentLightSensorRate + ", " +
"currentRate=" + lightSensorRate);
@@ -458,7 +527,7 @@ class AutomaticBrightnessController {
}
private void setAmbientLux(float lux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAmbientLux(" + lux + ")");
}
if (lux < 0) {
@@ -476,7 +545,7 @@ class AutomaticBrightnessController {
final float maxAmbientLux =
mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
}
@@ -490,7 +559,7 @@ class AutomaticBrightnessController {
}
private float calculateAmbientLux(long now, long horizon) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
}
final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +578,7 @@ class AutomaticBrightnessController {
break;
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
mAmbientLightRingBuffer.getTime(endIndex) + ", " +
mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +596,7 @@ class AutomaticBrightnessController {
final long startTime = eventTime - now;
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
"lux=" + lux + ", " +
"weight=" + weight);
@@ -536,7 +605,7 @@ class AutomaticBrightnessController {
sum += lux * weight;
endTime = startTime;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: " +
"totalWeight=" + totalWeight + ", " +
"newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +660,7 @@ class AutomaticBrightnessController {
final long timeWhenSensorWarmedUp =
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
"time=" + time + ", " +
"timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +671,7 @@ class AutomaticBrightnessController {
}
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
mAmbientLuxValid = true;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Initializing: " +
"mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
"mAmbientLux=" + mAmbientLux);
@@ -630,10 +699,10 @@ class AutomaticBrightnessController {
&& fastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
setAmbientLux(fastAmbientLux);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
+ ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ "mAmbientLux=" + mAmbientLux);
}
@@ -650,7 +719,7 @@ class AutomaticBrightnessController {
// weighted ambient lux or not.
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
@@ -662,7 +731,8 @@ class AutomaticBrightnessController {
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux);
+ float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ mForegroundAppCategory);
int newScreenAutoBrightness =
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +743,7 @@ class AutomaticBrightnessController {
if (mScreenAutoBrightness != -1
&& newScreenAutoBrightness > mScreenDarkeningThreshold
&& newScreenAutoBrightness < mScreenBrighteningThreshold) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
+ " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
}
@@ -681,8 +751,7 @@ class AutomaticBrightnessController {
}
if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAutoBrightness: " +
"mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
"newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -718,18 +787,11 @@ class AutomaticBrightnessController {
BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
}
- private void cancelBrightnessAdjustmentSample() {
- if (mBrightnessAdjustmentSamplePending) {
- mBrightnessAdjustmentSamplePending = false;
- mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
- }
- }
-
private void collectBrightnessAdjustmentSample() {
if (mBrightnessAdjustmentSamplePending) {
mBrightnessAdjustmentSamplePending = false;
if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
"lux=" + mAmbientLux + ", " +
"brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +807,68 @@ class AutomaticBrightnessController {
}
}
+ // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+ // foreground app's package name and category and correct the brightness accordingly.
+ private void registerForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ // This will not get called until the foreground app changes for the first time, so
+ // call it explicitly to get the current foreground app's info.
+ updateForegroundApp();
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+
+ private void unregisterForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ mForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ // Set the foreground app's package name and category, so brightness can be corrected per app.
+ private void updateForegroundApp() {
+ // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+ // thread and applied via this thread's handler synchronously.
+ BackgroundThread.getHandler().post(new Runnable() {
+ public void run() {
+ try {
+ // The foreground app is the top activity of the focused tasks stack.
+ final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+ if (info == null || info.topActivity == null) {
+ return;
+ }
+ final String packageName = info.topActivity.getPackageName();
+ // If the app didn't change, there's nothing to do. Otherwise, we have to
+ // update the category and re-apply the brightness correction.
+ if (mForegroundAppPackageName != null
+ && mForegroundAppPackageName.equals(packageName)) {
+ return;
+ }
+ mPendingForegroundAppPackageName = packageName;
+ ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
+ 0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
+ mPendingForegroundAppCategory = app.category;
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+ });
+ }
+
+ private void updateForegroundAppSync() {
+ mForegroundAppPackageName = mPendingForegroundAppPackageName;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = mPendingForegroundAppCategory;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ updateAutoBrightness(true /* sendUpdate */);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -764,6 +888,14 @@ class AutomaticBrightnessController {
case MSG_INVALIDATE_SHORT_TERM_MODEL:
invalidateShortTermModel();
break;
+
+ case MSG_UPDATE_FOREGROUND_APP:
+ updateForegroundApp();
+ break;
+
+ case MSG_UPDATE_FOREGROUND_APP_SYNC:
+ updateForegroundAppSync();
+ break;
}
}
}
@@ -784,6 +916,15 @@ class AutomaticBrightnessController {
}
};
+ // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+ // moving to top.
+ class TaskStackListenerImpl extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+ }
+ }
+
/** Callbacks to request updates to the display's power state. */
interface Callbacks {
void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d5e9ea..9fce644d6c4b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
import android.util.MathUtils;
import android.util.Pair;
@@ -42,11 +44,12 @@ import java.util.Arrays;
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = false;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
+ protected boolean mLoggingEnabled;
+
private static final Plog PLOG = Plog.createSystemPlog(TAG);
@Nullable
@@ -161,6 +164,22 @@ public abstract class BrightnessMappingStrategy {
}
/**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
* Sets the {@link BrightnessConfiguration}.
*
* @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@ public abstract class BrightnessMappingStrategy {
public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
/**
- * Returns the desired brightness of the display based on the current ambient lux.
+ * Returns the desired brightness of the display based on the current ambient lux, including
+ * any context-related corrections.
*
* The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
* brightness and 0 is the display at minimum brightness.
*
* @param lux The current ambient brightness in lux.
+ * @param packageName the foreground app package name.
+ * @param category the foreground app package category.
* @return The desired brightness of the display normalized to the range [0, 1.0].
*/
- public abstract float getBrightness(float lux);
+ public abstract float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category);
+
+ /**
+ * Returns the desired brightness of the display based on the current ambient lux.
+ *
+ * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
+ * brightness and 0 is the display at minimum brightness.
+ *
+ * @param lux The current ambient brightness in lux.
+ *
+ * @return The desired brightness of the display normalized to the range [0, 1.0].
+ */
+ public float getBrightness(float lux) {
+ return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+ }
/**
* Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@ public abstract class BrightnessMappingStrategy {
public abstract void dump(PrintWriter pw);
- private static float normalizeAbsoluteBrightness(int brightness) {
+ protected float normalizeAbsoluteBrightness(int brightness) {
brightness = MathUtils.constrain(brightness,
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
return (float) brightness / PowerManager.BRIGHTNESS_ON;
}
- private static Pair<float[], float[]> insertControlPoint(
+ private Pair<float[], float[]> insertControlPoint(
float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
final int idx = findInsertionPoint(luxLevels, lux);
final float[] newLuxLevels;
@@ -278,7 +315,7 @@ public abstract class BrightnessMappingStrategy {
* This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
* than val, then it will return the length of arr as the insertion point.
*/
- private static int findInsertionPoint(float[] arr, float val) {
+ private int findInsertionPoint(float[] arr, float val) {
for (int i = 0; i < arr.length; i++) {
if (val <= arr[i]) {
return i;
@@ -287,8 +324,8 @@ public abstract class BrightnessMappingStrategy {
return arr.length;
}
- private static void smoothCurve(float[] lux, float[] brightness, int idx) {
- if (DEBUG) {
+ private void smoothCurve(float[] lux, float[] brightness, int idx) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unsmoothed curve", lux, brightness);
}
float prevLux = lux[idx];
@@ -323,19 +360,19 @@ public abstract class BrightnessMappingStrategy {
prevBrightness = newBrightness;
brightness[i] = newBrightness;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("smoothed curve", lux, brightness);
}
}
- private static float permissibleRatio(float currLux, float prevLux) {
+ private float permissibleRatio(float currLux, float prevLux) {
return MathUtils.exp(MAX_GRAD
* (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
- MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
}
- private static float inferAutoBrightnessAdjustment(float maxGamma,
- float desiredBrightness, float currentBrightness) {
+ protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+ float currentBrightness) {
float adjustment = 0;
float gamma = Float.NaN;
// Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@ public abstract class BrightnessMappingStrategy {
adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
}
adjustment = MathUtils.constrain(adjustment, -1, +1);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@ public abstract class BrightnessMappingStrategy {
return adjustment;
}
- private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+ protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
float userLux, float userBrightness, float adjustment, float maxGamma) {
float[] newLux = lux;
float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unadjusted curve", newLux, newBrightness);
}
adjustment = MathUtils.constrain(adjustment, -1, 1);
float gamma = MathUtils.pow(maxGamma, -adjustment);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
}
@@ -382,7 +419,7 @@ public abstract class BrightnessMappingStrategy {
newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
}
if (userLux != -1) {
@@ -390,7 +427,7 @@ public abstract class BrightnessMappingStrategy {
userBrightness);
newLux = curve.first;
newBrightness = curve.second;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
// This is done for comparison.
curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@ public abstract class BrightnessMappingStrategy {
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
mUserBrightness = -1;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("simple mapping strategy");
}
computeSpline();
@@ -452,7 +489,8 @@ public abstract class BrightnessMappingStrategy {
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
return mSpline.interpolate(lux);
}
@@ -467,7 +505,7 @@ public abstract class BrightnessMappingStrategy {
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@ public abstract class BrightnessMappingStrategy {
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@ public abstract class BrightnessMappingStrategy {
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -507,7 +545,7 @@ public abstract class BrightnessMappingStrategy {
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@ public abstract class BrightnessMappingStrategy {
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
mDefaultConfig = config;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("physical mapping strategy");
}
mConfig = config;
@@ -629,7 +667,7 @@ public abstract class BrightnessMappingStrategy {
if (config.equals(mConfig)) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("brightness configuration");
}
mConfig = config;
@@ -638,9 +676,17 @@ public abstract class BrightnessMappingStrategy {
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
float nits = mBrightnessSpline.interpolate(lux);
float backlight = mNitsToBacklightSpline.interpolate(nits);
+ // Correct the brightness according to the current application and its category, but
+ // only if no user data point is set (as this will oevrride the user setting).
+ if (mUserLux == -1) {
+ backlight = correctBrightness(backlight, packageName, category);
+ } else if (mLoggingEnabled) {
+ Slog.d(TAG, "user point set, correction not applied");
+ }
return backlight;
}
@@ -655,7 +701,7 @@ public abstract class BrightnessMappingStrategy {
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@ public abstract class BrightnessMappingStrategy {
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@ public abstract class BrightnessMappingStrategy {
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -695,7 +741,7 @@ public abstract class BrightnessMappingStrategy {
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@ public abstract class BrightnessMappingStrategy {
Spline spline = Spline.createSpline(curve.first, curve.second);
return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
}
+
+ private float correctBrightness(float brightness, String packageName, int category) {
+ if (packageName != null) {
+ BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+ BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ return brightness;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 521fa236cf7d..b6c82d3a66e4 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -39,26 +39,31 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings.Secure;
+import android.provider.Settings.System;
import android.util.MathUtils;
import android.util.Slog;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
+import com.android.server.DisplayThread;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.DateTimeException;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
/**
* Controls the display's color transforms.
*/
-public final class ColorDisplayService extends SystemService
- implements ColorDisplayController.Callback {
+public final class ColorDisplayService extends SystemService {
private static final String TAG = "ColorDisplayService";
@@ -71,6 +76,7 @@ public final class ColorDisplayService extends SystemService
* The identity matrix, used if one of the given matrices is {@code null}.
*/
private static final float[] MATRIX_IDENTITY = new float[16];
+
static {
Matrix.setIdentityM(MATRIX_IDENTITY, 0);
}
@@ -90,10 +96,12 @@ public final class ColorDisplayService extends SystemService
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private ColorDisplayController mController;
+ private ColorDisplayController mNightDisplayController;
+ private ContentObserver mContentObserver;
private ValueAnimator mColorMatrixAnimator;
- private Boolean mIsActivated;
- private AutoMode mAutoMode;
+
+ private Boolean mIsNightDisplayActivated;
+ private NightDisplayAutoMode mNightDisplayAutoMode;
public ColorDisplayService(Context context) {
super(context);
@@ -186,42 +194,102 @@ public final class ColorDisplayService extends SystemService
private void setUp() {
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
- // Create a new controller for the current user and start listening for changes.
- mController = new ColorDisplayController(getContext(), mCurrentUser);
- mController.setListener(this);
+ mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
+
+ // Listen for external changes to any of the settings.
+ if (mContentObserver == null) {
+ mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null) {
+ switch (setting) {
+ case Secure.NIGHT_DISPLAY_ACTIVATED:
+ onNightDisplayActivated(mNightDisplayController.isActivated());
+ break;
+ case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+ onNightDisplayColorTemperatureChanged(
+ mNightDisplayController.getColorTemperature());
+ break;
+ case Secure.NIGHT_DISPLAY_AUTO_MODE:
+ onNightDisplayAutoModeChanged(
+ mNightDisplayController.getAutoMode());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+ onNightDisplayCustomStartTimeChanged(
+ mNightDisplayController.getCustomStartTime());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+ onNightDisplayCustomEndTimeChanged(
+ mNightDisplayController.getCustomEndTime());
+ break;
+ case System.DISPLAY_COLOR_MODE:
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ break;
+ case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+ case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
+ onAccessibilityTransformChanged();
+ break;
+ }
+ }
+ }
+ };
+ }
+ final ContentResolver cr = getContext().getContentResolver();
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
// Set the color mode, if valid, and immediately apply the updated tint matrix based on the
// existing activated state. This ensures consistency of tint across the color mode change.
- onDisplayColorModeChanged(mController.getColorMode());
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
// Reset the activated state.
- mIsActivated = null;
+ mIsNightDisplayActivated = null;
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
// Prepare color transformation matrix.
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
// Initialize the current auto mode.
- onAutoModeChanged(mController.getAutoMode());
+ onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
// Force the initialization current activated state.
- if (mIsActivated == null) {
- onActivated(mController.isActivated());
+ if (mIsNightDisplayActivated == null) {
+ onNightDisplayActivated(mNightDisplayController.isActivated());
}
}
private void tearDown() {
Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
- if (mController != null) {
- mController.setListener(null);
- mController = null;
+ getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+
+ if (mNightDisplayController != null) {
+ mNightDisplayController = null;
}
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (mColorMatrixAnimator != null) {
@@ -230,67 +298,61 @@ public final class ColorDisplayService extends SystemService
}
}
- @Override
- public void onActivated(boolean activated) {
- if (mIsActivated == null || mIsActivated != activated) {
+ private void onNightDisplayActivated(boolean activated) {
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
- mIsActivated = activated;
+ mIsNightDisplayActivated = activated;
- if (mAutoMode != null) {
- mAutoMode.onActivated(activated);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onActivated(activated);
}
applyTint(false);
}
}
- @Override
- public void onAutoModeChanged(int autoMode) {
- Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+ private void onNightDisplayAutoModeChanged(int autoMode) {
+ Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
- mAutoMode = new CustomAutoMode();
+ mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
} else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
- mAutoMode = new TwilightAutoMode();
+ mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
}
- if (mAutoMode != null) {
- mAutoMode.onStart();
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStart();
}
}
- @Override
- public void onCustomStartTimeChanged(LocalTime startTime) {
- Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+ private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
+ Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomStartTimeChanged(startTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
}
}
- @Override
- public void onCustomEndTimeChanged(LocalTime endTime) {
- Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+ private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
+ Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomEndTimeChanged(endTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
}
}
- @Override
- public void onColorTemperatureChanged(int colorTemperature) {
+ private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
setMatrix(colorTemperature, mMatrixNight);
applyTint(true);
}
- @Override
- public void onDisplayColorModeChanged(int mode) {
+ private void onDisplayColorModeChanged(int mode) {
if (mode == -1) {
return;
}
@@ -301,16 +363,15 @@ public final class ColorDisplayService extends SystemService
}
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
- : MATRIX_IDENTITY);
+ dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
+ ? mMatrixNight : MATRIX_IDENTITY);
}
- @Override
- public void onAccessibilityTransformChanged(boolean state) {
- onDisplayColorModeChanged(mController.getColorMode());
+ private void onAccessibilityTransformChanged() {
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
}
/**
@@ -338,7 +399,7 @@ public final class ColorDisplayService extends SystemService
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
- final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+ final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
if (immediate) {
dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
@@ -383,7 +444,7 @@ public final class ColorDisplayService extends SystemService
* Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
*
* @param colorTemperature color temperature in Kelvin
- * @param outTemp the 4x4 display transformation matrix for that color temperature
+ * @param outTemp the 4x4 display transformation matrix for that color temperature
*/
private void setMatrix(int colorTemperature, float[] outTemp) {
if (outTemp.length != 16) {
@@ -412,7 +473,8 @@ public final class ColorDisplayService extends SystemService
* @param compareTime the LocalDateTime to compare against
* @return the prior LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -427,7 +489,8 @@ public final class ColorDisplayService extends SystemService
* @param compareTime the LocalDateTime to compare against
* @return the next LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -440,13 +503,47 @@ public final class ColorDisplayService extends SystemService
return dtm.isDeviceColorManaged();
}
- private abstract class AutoMode implements ColorDisplayController.Callback {
+ /**
+ * Returns the last time the night display transform activation state was changed, or {@link
+ * LocalDateTime#MIN} if night display has never been activated.
+ */
+ private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+ final ContentResolver cr = getContext().getContentResolver();
+ final String lastActivatedTime = Secure.getStringForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
+ if (lastActivatedTime != null) {
+ try {
+ return LocalDateTime.parse(lastActivatedTime);
+ } catch (DateTimeParseException ignored) {
+ }
+ // Uses the old epoch time.
+ try {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+ ZoneId.systemDefault());
+ } catch (DateTimeException | NumberFormatException ignored) {
+ }
+ }
+ return LocalDateTime.MIN;
+ }
+
+ private abstract class NightDisplayAutoMode {
+
+ public abstract void onActivated(boolean activated);
+
public abstract void onStart();
public abstract void onStop();
+
+ public void onCustomStartTimeChanged(LocalTime startTime) {
+ }
+
+ public void onCustomEndTimeChanged(LocalTime endTime) {
+ }
}
- private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+ private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
+ AlarmManager.OnAlarmListener {
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
@@ -456,7 +553,7 @@ public final class ColorDisplayService extends SystemService
private LocalDateTime mLastActivatedTime;
- CustomAutoMode() {
+ CustomNightDisplayAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mTimeChangedReceiver = new BroadcastReceiver() {
@Override
@@ -476,15 +573,15 @@ public final class ColorDisplayService extends SystemService
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
&& (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
- activate = mController.isActivated();
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
- updateNextAlarm(mIsActivated, now);
+ updateNextAlarm(mIsNightDisplayActivated, now);
}
private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -502,10 +599,10 @@ public final class ColorDisplayService extends SystemService
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
- mStartTime = mController.getCustomStartTime();
- mEndTime = mController.getCustomEndTime();
+ mStartTime = mNightDisplayController.getCustomStartTime();
+ mEndTime = mNightDisplayController.getCustomEndTime();
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated();
@@ -521,7 +618,7 @@ public final class ColorDisplayService extends SystemService
@Override
public void onActivated(boolean activated) {
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
updateNextAlarm(activated, LocalDateTime.now());
}
@@ -546,11 +643,13 @@ public final class ColorDisplayService extends SystemService
}
}
- private class TwilightAutoMode extends AutoMode implements TwilightListener {
+ private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
+ TwilightListener {
private final TwilightManager mTwilightManager;
+ private LocalDateTime mLastActivatedTime;
- TwilightAutoMode() {
+ TwilightNightDisplayAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
@@ -562,26 +661,31 @@ public final class ColorDisplayService extends SystemService
}
boolean activate = state.isNight();
- final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
- if (lastActivatedTime != null) {
+ if (mLastActivatedTime != null) {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime sunrise = state.sunrise();
final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
- ^ lastActivatedTime.isBefore(sunset))) {
- activate = mController.isActivated();
+ if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
+ ^ mLastActivatedTime.isBefore(sunset))) {
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
}
@Override
+ public void onActivated(boolean activated) {
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
+ }
+
+ @Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -590,10 +694,7 @@ public final class ColorDisplayService extends SystemService
@Override
public void onStop() {
mTwilightManager.unregisterListener(this);
- }
-
- @Override
- public void onActivated(boolean activated) {
+ mLastActivatedTime = null;
}
@Override
@@ -624,6 +725,7 @@ public final class ColorDisplayService extends SystemService
}
private final class BinderService extends IColorDisplayManager.Stub {
+
@Override
public boolean isDeviceColorManaged() {
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index cf8d21b66417..b1ba05c035b3 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1296,58 +1296,21 @@ public final class DisplayManagerService extends SystemService {
return;
}
display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
-
+ final int viewportType;
// Update the corresponding viewport.
- DisplayViewport internalViewport = getInternalViewportLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
- populateViewportLocked(internalViewport, display, device);
- }
- DisplayViewport externalViewport = getExternalViewportLocked();
- if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
- populateViewportLocked(externalViewport, display, device);
- } else if (!externalViewport.valid) {
- // TODO (b/116850516) move this logic into InputReader
- externalViewport.copyFrom(internalViewport);
- externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
- }
-
- if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
- final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
- populateViewportLocked(viewport, display, device);
- }
- }
-
- /** Get the virtual device viewport that has the specified uniqueId.
- * If such viewport does not exist, create it. */
- private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
- DisplayViewport viewport;
- final int count = mViewports.size();
- for (int i = 0; i < count; i++) {
- viewport = mViewports.get(i);
- if (uniqueId.equals(viewport.uniqueId)) {
- if (viewport.type != VIEWPORT_VIRTUAL) {
- Slog.wtf(TAG, "Found a viewport with uniqueId '" + uniqueId
- + "' but it has type " + DisplayViewport.typeToString(viewport.type)
- + " (expected VIRTUAL)");
- continue;
- }
- return viewport;
- }
+ viewportType = VIEWPORT_INTERNAL;
+ } else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+ viewportType = VIEWPORT_EXTERNAL;
+ } else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
+ && !TextUtils.isEmpty(info.uniqueId)) {
+ viewportType = VIEWPORT_VIRTUAL;
+ } else {
+ Slog.wtf(TAG, "Unable to populate viewport for display device: " + info);
+ return;
}
- viewport = new DisplayViewport();
- viewport.uniqueId = uniqueId;
- viewport.type = VIEWPORT_VIRTUAL;
- mViewports.add(viewport);
- return viewport;
- }
-
- private DisplayViewport getInternalViewportLocked() {
- return getViewportByTypeLocked(VIEWPORT_INTERNAL);
- }
-
- private DisplayViewport getExternalViewportLocked() {
- return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+ populateViewportLocked(viewportType, display.getDisplayIdLocked(), device, info.uniqueId);
}
/**
@@ -1355,35 +1318,44 @@ public final class DisplayManagerService extends SystemService {
* @param viewportType - either INTERNAL or EXTERNAL
* @return the viewport with the requested type
*/
- private DisplayViewport getViewportByTypeLocked(int viewportType) {
- // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
- // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
- // Creates the viewport if none exists.
- if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+ private DisplayViewport getViewportLocked(int viewportType, String uniqueId) {
+ if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL
+ && viewportType != VIEWPORT_VIRTUAL) {
Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
+ DisplayViewport.typeToString(viewportType));
return null;
}
+
+ // Only allow a single INTERNAL or EXTERNAL viewport by forcing their uniqueIds
+ // to be identical (in particular, empty).
+ // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+ if (viewportType != VIEWPORT_VIRTUAL) {
+ uniqueId = "";
+ }
+
DisplayViewport viewport;
final int count = mViewports.size();
for (int i = 0; i < count; i++) {
viewport = mViewports.get(i);
- if (viewport.type == viewportType) {
+ if (viewport.type == viewportType && uniqueId.equals(viewport.uniqueId)) {
return viewport;
}
}
+ // Creates the viewport if none exists.
viewport = new DisplayViewport();
viewport.type = viewportType;
+ viewport.uniqueId = uniqueId;
mViewports.add(viewport);
return viewport;
}
- private static void populateViewportLocked(DisplayViewport viewport,
- LogicalDisplay display, DisplayDevice device) {
- viewport.valid = true;
- viewport.displayId = display.getDisplayIdLocked();
+ private void populateViewportLocked(int viewportType,
+ int displayId, DisplayDevice device, String uniqueId) {
+ final DisplayViewport viewport = getViewportLocked(viewportType, uniqueId);
device.populateViewportLocked(viewport);
+ viewport.valid = true;
+ viewport.displayId = displayId;
}
private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
@@ -2172,6 +2144,14 @@ public final class DisplayManagerService extends SystemService {
mContext.getPackageName());
}
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+ }
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1eece09..abbfc7b18f94 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
package com.android.server.display;
import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.NumberFormatException;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@ class DisplayManagerShellCommand extends ShellCommand {
return setBrightness();
case "reset-brightness-configuration":
return resetBrightnessConfiguration();
+ case "ab-logging-enable":
+ return setAutoBrightnessLoggingEnabled(true);
+ case "ab-logging-disable":
+ return setAutoBrightnessLoggingEnabled(false);
default:
return handleDefaultCommands(cmd);
}
@@ -62,6 +61,10 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
pw.println(" reset-brightness-configuration");
pw.println(" Reset the brightness to its default configuration.");
+ pw.println(" ab-logging-enable");
+ pw.println(" Enable auto-brightness logging.");
+ pw.println(" ab-logging-disable");
+ pw.println(" Disable auto-brightness logging.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -89,4 +92,9 @@ class DisplayManagerShellCommand extends ShellCommand {
mService.resetBrightnessConfiguration();
return 0;
}
+
+ private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+ mService.setAutoBrightnessLoggingEnabled(enabled);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270bfda7e..c9ed9f7cea43 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,6 +523,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
mBrightnessTracker.onSwitchUser(newUserId);
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.onSwitchUser(newUserId);
+ }
}
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1836,4 +1839,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mHandler.sendMessage(msg);
}
}
+
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef621a55d..9aec43b6eaed 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@ import android.hardware.display.WifiDisplay;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Pair;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Manages persistent state recorded by the display manager service as an XML file.
* Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@ final class PersistentDataStore {
private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
- private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
- private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
private static final String ATTR_USER_SERIAL = "user-serial";
private static final String ATTR_PACKAGE_NAME = "package-name";
private static final String ATTR_TIME_STAMP = "timestamp";
- private static final String ATTR_LUX = "lux";
- private static final String ATTR_NITS = "nits";
- private static final String ATTR_DESCRIPTION = "description";
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@ final class PersistentDataStore {
}
try {
- BrightnessConfiguration config = loadConfigurationFromXml(parser);
+ BrightnessConfiguration config =
+ BrightnessConfiguration.loadFromXml(parser);
if (userSerial >= 0 && config != null) {
mConfigurations.put(userSerial, config);
if (timeStamp != -1) {
@@ -663,56 +656,6 @@ final class PersistentDataStore {
}
}
- private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- String description = null;
- Pair<float[], float[]> curve = null;
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
- description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
- curve = loadCurveFromXml(parser);
- }
- }
- if (curve == null) {
- return null;
- }
- final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
- curve.first, curve.second);
- builder.setDescription(description);
- return builder.build();
- }
-
- private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- List<Float> luxLevels = new ArrayList<>();
- List<Float> nitLevels = new ArrayList<>();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
- luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
- nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
- }
- }
- final int N = luxLevels.size();
- float[] lux = new float[N];
- float[] nits = new float[N];
- for (int i = 0; i < N; i++) {
- lux[i] = luxLevels.get(i);
- nits[i] = nitLevels.get(i);
- }
- return Pair.create(lux, nits);
- }
-
- private static float loadFloat(String val) {
- try {
- return Float.parseFloat(val);
- } catch (NullPointerException | NumberFormatException e) {
- Slog.e(TAG, "Failed to parse float loading brightness config", e);
- return Float.NEGATIVE_INFINITY;
- }
- }
-
public void saveToXml(XmlSerializer serializer) throws IOException {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
@@ -728,27 +671,11 @@ final class PersistentDataStore {
if (timestamp != -1) {
serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
}
- saveConfigurationToXml(serializer, config);
+ config.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
}
}
- private static void saveConfigurationToXml(XmlSerializer serializer,
- BrightnessConfiguration config) throws IOException {
- serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
- if (config.getDescription() != null) {
- serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
- }
- final Pair<float[], float[]> curve = config.getCurve();
- for (int i = 0; i < curve.first.length; i++) {
- serializer.startTag(null, TAG_BRIGHTNESS_POINT);
- serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
- serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
- serializer.endTag(null, TAG_BRIGHTNESS_POINT);
- }
- serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
- }
-
public void dump(final PrintWriter pw, final String prefix) {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0d3fdfb8f91..f468c0bf6652 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -81,8 +81,10 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import libcore.util.EmptyArray;
/**
@@ -94,6 +96,31 @@ public class HdmiControlService extends SystemService {
private final Locale HONG_KONG = new Locale("zh", "HK");
private final Locale MACAU = new Locale("zh", "MO");
+ private static final Map<String, String> mTerminologyToBibliographicMap;
+ static {
+ mTerminologyToBibliographicMap = new HashMap<>();
+ // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
+ mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
+ mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
+ mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
+ mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
+ mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
+ mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
+ mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
+ mTerminologyToBibliographicMap.put("deu", "ger"); // German
+ mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
+ mTerminologyToBibliographicMap.put("fra", "fre"); // French
+ mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
+ mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
+ mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
+ mTerminologyToBibliographicMap.put("msa", "may"); // Malay
+ mTerminologyToBibliographicMap.put("fas", "per"); // Persian
+ mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
+ mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
+ mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
+ mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+ }
+
static final String PERMISSION = "android.permission.HDMI_CEC";
// The reason code to initiate initializeCec().
@@ -177,7 +204,18 @@ public class HdmiControlService extends SystemService {
// Chinese used in Taiwan/Hong Kong/Macau.
return "chi";
} else {
- return locale.getISO3Language();
+ String language = locale.getISO3Language();
+
+ // locale.getISO3Language() returns terminology code and need to
+ // send it as bibliographic code instead since the Bibliographic
+ // codes of ISO/FDIS 639-2 shall be used.
+ // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+ // But, as it depends on the locale, is not handled here.
+ if (mTerminologyToBibliographicMap.containsKey(language)) {
+ language = mTerminologyToBibliographicMap.get(language);
+ }
+
+ return language;
}
}
}
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index 205d40b9e5cd..fd87e3d32b1d 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -18,6 +18,8 @@ package com.android.server.infra;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.internal.infra.AbstractRemoteService;
+
import java.io.PrintWriter;
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e7c3c7bbe21b..d96b6cba119b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,11 +1951,6 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
- private int getPointerDisplayId() {
- return mWindowManagerCallbacks.getPointerDisplayId();
- }
-
- // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2022,8 +2017,6 @@ public class InputManagerService extends IInputManager.Stub
KeyEvent event, int policyFlags);
public int getPointerLayer();
-
- public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 28a6ba4ceb1d..fb6eaa0b85e6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -73,6 +73,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
@@ -675,13 +676,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private final int mHardKeyboardBehavior;
/**
- * Whether we temporarily allow IMEs implemented in instant apps to run for testing.
- *
- * <p>Note: This is quite dangerous. Don't forget to reset after you finish testing.</p>
- */
- private boolean mBindInstantServiceAllowed = false;
-
- /**
* Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
* internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
* will not affect those tasks that are already posted.
@@ -1135,8 +1129,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
- getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
- getChangingUserId());
+ PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
// No need to lock this because we access it only on getRegisteredHandler().
if (!services.isEmpty()) {
mImePackageAppeared = true;
@@ -1684,9 +1677,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
- if (mBindInstantServiceAllowed) {
- flags |= Context.BIND_ALLOW_INSTANT;
- }
return mContext.bindServiceAsUser(service, conn, flags,
new UserHandle(mSettings.getCurrentUserId()));
}
@@ -1696,6 +1686,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return getInputMethodList(false /* isVrOnly */);
}
+ @Override
public List<InputMethodInfo> getVrInputMethodList() {
return getInputMethodList(true /* isVrOnly */);
}
@@ -3285,7 +3276,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int packageNum = packageInfos.length;
for (int i = 0; i < packageNum; ++i) {
if (packageInfos[i].equals(imi.getPackageName())) {
- mFileManager.addInputMethodSubtypes(imi, subtypes);
+ if (subtypes.length > 0) {
+ mFileManager.addInputMethodSubtypes(imi, subtypes);
+ } else {
+ mFileManager.deleteAllInputMethodSubtypes(imi.getId());
+ }
final long ident = Binder.clearCallingIdentity();
try {
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -3631,16 +3626,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
- @PackageManager.ResolveInfoFlags
- private int getComponentMatchingFlags(@PackageManager.ResolveInfoFlags int baseFlags) {
- synchronized (mMethodMap) {
- if (mBindInstantServiceAllowed) {
- baseFlags |= PackageManager.MATCH_INSTANT;
- }
- return baseFlags;
- }
- }
-
@GuardedBy("mMethodMap")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
@@ -3664,8 +3649,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// services depending on the unlock state for the specified user.
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- getComponentMatchingFlags(PackageManager.GET_META_DATA
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
+ PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
mSettings.getCurrentUserId());
final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
@@ -3707,8 +3691,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// conservative, but it seems we cannot use it for now (Issue 35176630).
final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
- mSettings.getCurrentUserId());
+ PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
final int N = allInputMethodServices.size();
for (int i = 0; i < N; ++i) {
final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -4318,19 +4301,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
? new File(Environment.getDataDirectory(), SYSTEM_PATH)
: Environment.getUserSystemDirectory(userId);
final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
- if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
- Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
- }
final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
- if (!subtypeFile.exists()) {
- // If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
- } else {
- readAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
- }
+ readAdditionalInputMethodSubtypes(mAdditionalSubtypesMap,
+ mAdditionalInputMethodSubtypeFile);
}
private void deleteAllInputMethodSubtypes(String imiId) {
@@ -4370,6 +4344,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static void writeAdditionalInputMethodSubtypes(
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
ArrayMap<String, InputMethodInfo> methodMap) {
+ if (allSubtypes.isEmpty()) {
+ if (subtypesFile.exists()) {
+ subtypesFile.delete();
+ }
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (parentDir != null && FileUtils.listFilesOrEmpty(parentDir).length == 0) {
+ if (!parentDir.delete()) {
+ Slog.e(TAG, "Failed to delete the empty parent directory " + parentDir);
+ }
+ }
+ return;
+ }
+
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (!parentDir.exists() && !parentDir.mkdirs()) {
+ Slog.e(TAG, "Failed to create a parent directory " + parentDir);
+ return;
+ }
+
// Safety net for the case that this function is called before methodMap is set.
final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
FileOutputStream fos = null;
@@ -4426,6 +4419,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
+ if (!subtypesFile.exists()) {
+ // Not having the file means there is no additional subtype.
+ return;
+ }
try (final FileInputStream fis = subtypesFile.openRead()) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, StandardCharsets.UTF_8.name());
@@ -4606,8 +4603,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
p.println("Current Input Method Manager state:");
int N = mMethodList.size();
- p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount
- + " mBindInstantServiceAllowed=" + mBindInstantServiceAllowed);
+ p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
for (int i=0; i<N; i++) {
InputMethodInfo info = mMethodList.get(i);
p.println(" InputMethod #" + i + ":");
@@ -4719,9 +4715,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if ("refresh_debug_properties".equals(cmd)) {
return refreshDebugProperties();
}
- if ("set-bind-instant-service-allowed".equals(cmd)) {
- return setBindInstantServiceAllowed();
- }
// For existing "adb shell ime <command>".
if ("ime".equals(cmd)) {
@@ -4752,12 +4745,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@ShellCommandResult
- private int setBindInstantServiceAllowed() {
- return mService.handleSetBindInstantServiceAllowed(this);
- }
-
- @BinderThread
- @ShellCommandResult
private int refreshDebugProperties() {
DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
return ShellCommandResult.SUCCESS;
@@ -4774,9 +4761,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
pw.println(" Synonym of dumpsys.");
pw.println(" ime <command> [options]");
pw.println(" Manipulate IMEs. Run \"ime help\" for details.");
- pw.println(" set-bind-instant-service-allowed true|false ");
- pw.println(" Set whether binding to services provided by instant apps is "
- + "allowed.");
}
}
@@ -4825,53 +4809,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Shell command handlers:
/**
- * Handles {@code adb shell cmd input_method set-bind-instant-service-allowed}.
- *
- * @param shellCommand {@link ShellCommand} object that is handling this command.
- * @return Exit code of the command.
- */
- @BinderThread
- @RequiresPermission(android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
- @ShellCommandResult
- private int handleSetBindInstantServiceAllowed(@NonNull ShellCommand shellCommand) {
- final String allowedString = shellCommand.getNextArgRequired();
- if (allowedString == null) {
- shellCommand.getErrPrintWriter().println("Error: no true/false specified");
- return ShellCommandResult.FAILURE;
- }
- final boolean allowed = Boolean.parseBoolean(allowedString);
- synchronized (mMethodMap) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
- != PackageManager.PERMISSION_GRANTED) {
- shellCommand.getErrPrintWriter().print(
- "Caller must have MANAGE_BIND_INSTANT_SERVICE permission");
- return ShellCommandResult.FAILURE;
- }
-
- if (mBindInstantServiceAllowed == allowed) {
- // Nothing to do.
- return ShellCommandResult.SUCCESS;
- }
- mBindInstantServiceAllowed = allowed;
-
- // Rebuild everything.
- final long ident = Binder.clearCallingIdentity();
- try {
- // Reset the current IME
- resetSelectedInputMethodAndSubtypeLocked(null);
- // Also reset the settings of the current IME
- mSettings.putSelectedInputMethod(null);
- buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
- updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- return ShellCommandResult.SUCCESS;
- }
-
- /**
* Handles {@code adb shell ime list}.
* @param shellCommand {@link ShellCommand} object that is handling this command.
* @return Exit code of the command.
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index ee1d8476c471..827c0f1df71a 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -44,17 +44,11 @@ class JobConcurrencyManager {
* We manipulate this array until we arrive at what jobs should be running on
* what JobServiceContext.
*/
- JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+ JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
- /**
- * Indicates whether we need to act on this jobContext id
- */
- boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+ boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT];
- /**
- * The uid whose jobs we would like to assign to a context.
- */
- int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+ int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
@@ -99,28 +93,30 @@ class JobConcurrencyManager {
break;
}
- JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
- boolean[] act = mTmpAssignAct;
- int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
- int numActive = 0;
- int numForeground = 0;
+ // To avoid GC churn, we recycle the arrays.
+ JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
+ boolean[] slotChanged = mRecycledSlotChanged;
+ int[] preferredUidForContext = mRecycledPreferredUidForContext;
+
+ int numTotalRunningJobs = 0;
+ int numForegroundJobs = 0;
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = mService.mActiveServices.get(i);
final JobStatus status = js.getRunningJobLocked();
if ((contextIdToJobMap[i] = status) != null) {
- numActive++;
+ numTotalRunningJobs++;
if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
- act[i] = false;
+ slotChanged[i] = false;
preferredUidForContext[i] = js.getPreferredUid();
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
}
for (int i=0; i<pendingJobs.size(); i++) {
- JobStatus nextPending = pendingJobs.get(i);
+ final JobStatus nextPending = pendingJobs.get(i);
// If job is already running, go to next job.
int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -131,23 +127,32 @@ class JobConcurrencyManager {
final int priority = mService.evaluateJobPriorityLocked(nextPending);
nextPending.lastEvaluatedPriority = priority;
- // Find a context for nextPending. The context should be available OR
+ // Find an available slot for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
- int minPriority = Integer.MAX_VALUE;
- int minPriorityContextId = -1;
+ int minPriorityForPreemption = Integer.MAX_VALUE;
+ int selectedContextId = -1;
+ boolean startingJob = false;
for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
JobStatus job = contextIdToJobMap[j];
int preferredUid = preferredUidForContext[j];
if (job == null) {
- if ((numActive < mService.mMaxActiveJobs ||
- (priority >= JobInfo.PRIORITY_TOP_APP &&
- numForeground < mConstants.FG_JOB_COUNT)) &&
- (preferredUid == nextPending.getUid() ||
- preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+ final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
+ final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
+ && (numForegroundJobs < mConstants.FG_JOB_COUNT);
+ final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
+ || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
+
+ // TODO: The following check is slightly wrong.
+ // Depending on how the pending jobs are sorted, we sometimes cap the total
+ // job count at mMaxActiveJobs (when all jobs are FG jobs), or
+ // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
+ // and then FG_JOB_COUNT FG jobs.)
+ if ((totalCountOk || fgCountOk) && preferredUidOkay) {
// This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here.
- minPriorityContextId = j;
+ selectedContextId = j;
+ startingJob = true;
break;
}
// No job on this context, but nextPending can't run here because
@@ -158,30 +163,39 @@ class JobConcurrencyManager {
if (job.getUid() != nextPending.getUid()) {
continue;
}
- if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+
+ final int jobPriority = mService.evaluateJobPriorityLocked(job);
+ if (jobPriority >= nextPending.lastEvaluatedPriority) {
continue;
}
- if (minPriority > nextPending.lastEvaluatedPriority) {
- minPriority = nextPending.lastEvaluatedPriority;
- minPriorityContextId = j;
+
+ // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
+ if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
+ minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+ selectedContextId = j;
+ // In this case, we're just going to preempt a low priority job, we're not
+ // actually starting a job, so don't set startingJob.
}
}
- if (minPriorityContextId != -1) {
- contextIdToJobMap[minPriorityContextId] = nextPending;
- act[minPriorityContextId] = true;
- numActive++;
+ if (selectedContextId != -1) {
+ contextIdToJobMap[selectedContextId] = nextPending;
+ slotChanged[selectedContextId] = true;
+ }
+ if (startingJob) {
+ // Increase the counters when we're going to start a job.
+ numTotalRunningJobs++;
if (priority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
}
- tracker.noteConcurrency(numActive, numForeground);
+ tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false;
- if (act[i]) {
+ if (slotChanged[i]) {
JobStatus js = activeServices.get(i).getRunningJobLocked();
if (js != null) {
if (DEBUG) {
@@ -195,7 +209,7 @@ class JobConcurrencyManager {
final JobStatus pendingJob = contextIdToJobMap[i];
if (DEBUG) {
Slog.d(TAG, "About to run job on context "
- + String.valueOf(i) + ", job: " + pendingJob);
+ + i + ", job: " + pendingJob);
}
for (int ic=0; ic<controllers.size(); ic++) {
controllers.get(ic).prepareForExecutionLocked(pendingJob);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 10dc156fbc01..3f9d928e1986 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1261,6 +1261,9 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public void onDeviceIdleStateChanged(boolean deviceIdle) {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Doze state changed: " + deviceIdle);
+ }
if (deviceIdle) {
// When becoming idle, make sure no jobs are actively running,
// except those using the idle exemption flag.
@@ -1829,6 +1832,9 @@ public class JobSchedulerService extends com.android.server.SystemService
}
} break;
case MSG_CHECK_JOB:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB");
+ }
if (mReportedActive) {
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLocked();
@@ -1838,6 +1844,9 @@ public class JobSchedulerService extends com.android.server.SystemService
}
break;
case MSG_CHECK_JOB_GREEDY:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
+ }
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 84bb13ec92d3..ee60daa20837 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,7 +45,12 @@ public interface NotificationDelegate {
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
- void onNotificationSmartRepliesAdded(String key, int replyCount);
+
+ /**
+ * Notifies that smart replies and actions have been added to the UI.
+ */
+ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+ boolean generatedByAssistant);
/**
* Notifies a smart reply is sent.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bad259bafeb8..2359c2a02863 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -762,7 +762,13 @@ public class NotificationManagerService extends SystemService {
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(actionIndex)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+ .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+ (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+ == action.getSemanticAction()) ? 1 : 0)
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ generatedByAssistant ? 1 : 0));
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -836,20 +842,13 @@ public class NotificationManagerService extends SystemService {
// Report to usage stats that notification was made visible
if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
reportSeen(r);
-
- // If the newly visible notification has smart replies
- // then log that the user has seen them.
- if (r.getNumSmartRepliesAdded() > 0
- && !r.hasSeenSmartReplies()) {
- r.setSeenSmartReplies(true);
- LogMaker logMaker = r.getLogMaker()
- .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
- .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
- r.getNumSmartRepliesAdded());
- mMetricsLogger.write(logMaker);
- }
}
r.setVisibility(true, nv.rank, nv.count);
+ // hasBeenVisiblyExpanded must be called after updating the expansion state of
+ // the NotificationRecord to ensure the expansion state is up-to-date.
+ if (r.hasBeenVisiblyExpanded()) {
+ logSmartSuggestionsVisible(r);
+ }
maybeRecordInterruptionLocked(r);
nv.recycle();
}
@@ -873,6 +872,11 @@ public class NotificationManagerService extends SystemService {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.stats.onExpansionChanged(userAction, expanded);
+ // hasBeenVisiblyExpanded must be called after updating the expansion state of
+ // the NotificationRecord to ensure the expansion state is up-to-date.
+ if (r.hasBeenVisiblyExpanded()) {
+ logSmartSuggestionsVisible(r);
+ }
final long now = System.currentTimeMillis();
if (userAction) {
MetricsLogger.action(r.getItemLogMaker()
@@ -908,11 +912,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+ public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+ int smartActionCount, boolean generatedByAssistant) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
- r.setNumSmartRepliesAdded(replyCount);
+ r.setNumSmartRepliesAdded(smartReplyCount);
+ r.setNumSmartActionsAdded(smartActionCount);
+ r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
}
}
}
@@ -920,6 +927,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
boolean generatedByAssistant) {
+
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -946,6 +954,26 @@ public class NotificationManagerService extends SystemService {
}
};
+ @VisibleForTesting
+ void logSmartSuggestionsVisible(NotificationRecord r) {
+ // If the newly visible notification has smart suggestions
+ // then log that the user has seen them.
+ if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
+ && !r.hasSeenSmartReplies()) {
+ r.setSeenSmartReplies(true);
+ LogMaker logMaker = r.getLogMaker()
+ .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
+ r.getNumSmartRepliesAdded())
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
+ r.getNumSmartActionsAdded())
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ r.getSuggestionsGeneratedByAssistant());
+ mMetricsLogger.write(logMaker);
+ }
+ }
+
@GuardedBy("mNotificationLock")
private void clearSoundLocked() {
mSoundNotificationKey = null;
@@ -1924,9 +1952,11 @@ public class NotificationManagerService extends SystemService {
*/
@GuardedBy("mNotificationLock")
protected void reportSeen(NotificationRecord r) {
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- getRealUserId(r.sbn.getUserId()),
- UsageEvents.Event.NOTIFICATION_SEEN);
+ if (!r.isProxied()) {
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ getRealUserId(r.sbn.getUserId()),
+ UsageEvents.Event.NOTIFICATION_SEEN);
+ }
}
protected int calculateSuppressedVisualEffects(Policy incomingPolicy, Policy currPolicy,
@@ -2264,6 +2294,26 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public boolean areAppOverlaysAllowed(String pkg) {
+ return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
+ checkCallerIsSystemOrSameApp(pkg);
+
+ return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+ }
+
+ @Override
+ public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+ checkCallerIsSystem();
+
+ mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+ handleSavePolicyFile();
+ }
+
+ @Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -2632,6 +2682,10 @@ public class NotificationManagerService extends SystemService {
* Note that since notification posting is done asynchronously, this will not return
* notifications that are in the process of being posted.
*
+ * From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as
+ * an app's notification delegate via
+ * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+ *
* @returns A list of all the package's notifications, in natural order.
*/
@Override
@@ -2674,16 +2728,18 @@ public class NotificationManagerService extends SystemService {
private StatusBarNotification sanitizeSbn(String pkg, int userId,
StatusBarNotification sbn) {
- if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
- // We could pass back a cloneLight() but clients might get confused and
- // try to send this thing back to notify() again, which would not work
- // very well.
- return new StatusBarNotification(
- sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- sbn.getNotification().clone(),
- sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ if (sbn.getUserId() == userId) {
+ if (sbn.getPackageName().equals(pkg) || sbn.getOpPkg().equals(pkg)) {
+ // We could pass back a cloneLight() but clients might get confused and
+ // try to send this thing back to notify() again, which would not work
+ // very well.
+ return new StatusBarNotification(
+ sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ sbn.getNotification().clone(),
+ sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ }
}
return null;
}
@@ -4002,7 +4058,7 @@ public class NotificationManagerService extends SystemService {
final ArraySet<ComponentName> listeners =
mListenersDisablingEffects.valueAt(i);
for (int j = 0; j < listeners.size(); j++) {
- final ComponentName componentName = listeners.valueAt(i);
+ final ComponentName componentName = listeners.valueAt(j);
componentName.writeToProto(proto,
ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
}
@@ -4147,8 +4203,8 @@ public class NotificationManagerService extends SystemService {
final int listenerSize = listeners.size();
for (int j = 0; j < listenerSize; j++) {
- if (i > 0) pw.print(',');
- final ComponentName listener = listeners.valueAt(i);
+ if (j > 0) pw.print(',');
+ final ComponentName listener = listeners.valueAt(j);
if (listener != null) {
pw.print(listener);
}
@@ -4388,7 +4444,7 @@ public class NotificationManagerService extends SystemService {
notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
}
- if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
if (fullscreenIntentPermission != PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 39451d40a97e..e2c64ca459a8 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -151,7 +151,6 @@ public final class NotificationRecord {
private int mSuppressedVisualEffects = 0;
private String mUserExplanation;
- private String mPeopleExplanation;
private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
@@ -178,6 +177,8 @@ public final class NotificationRecord {
private boolean mTextChanged;
private boolean mRecordedInterruption;
private int mNumberOfSmartRepliesAdded;
+ private int mNumberOfSmartActionsAdded;
+ private boolean mSuggestionsGeneratedByAssistant;
private boolean mHasSeenSmartReplies;
/**
* Whether this notification (and its channels) should be considered user locked. Used in
@@ -1140,6 +1141,22 @@ public final class NotificationRecord {
return mNumberOfSmartRepliesAdded;
}
+ public void setNumSmartActionsAdded(int noActions) {
+ mNumberOfSmartActionsAdded = noActions;
+ }
+
+ public int getNumSmartActionsAdded() {
+ return mNumberOfSmartActionsAdded;
+ }
+
+ public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
+ mSuggestionsGeneratedByAssistant = generatedByAssistant;
+ }
+
+ public boolean getSuggestionsGeneratedByAssistant() {
+ return mSuggestionsGeneratedByAssistant;
+ }
+
public boolean hasSeenSmartReplies() {
return mHasSeenSmartReplies;
}
@@ -1148,6 +1165,13 @@ public final class NotificationRecord {
mHasSeenSmartReplies = hasSeenSmartReplies;
}
+ /**
+ * Returns whether this notification has been visible and expanded at the same time.
+ */
+ public boolean hasBeenVisiblyExpanded() {
+ return stats.hasBeenVisiblyExpanded();
+ }
+
public void setSystemGeneratedSmartActions(
ArrayList<Notification.Action> systemGeneratedSmartActions) {
mSystemGeneratedSmartActions = systemGeneratedSmartActions;
@@ -1166,6 +1190,13 @@ public final class NotificationRecord {
}
/**
+ * Returns whether this notification was posted by a secondary app
+ */
+ public boolean isProxied() {
+ return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+ }
+
+ /**
* @return all {@link Uri} that should have permission granted to whoever
* will be rendering it. This list has already been vetted to only
* include {@link Uri} that the enqueuing app can grant.
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e40dad6e83f1..d630b9ab54d6 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -916,6 +916,13 @@ public class NotificationUsageStats {
updateVisiblyExpandedStats();
}
+ /**
+ * Returns whether this notification has been visible and expanded at the same.
+ */
+ public boolean hasBeenVisiblyExpanded() {
+ return posttimeToFirstVisibleExpansionMs >= 0;
+ }
+
private void updateVisiblyExpandedStats() {
long elapsedNowMs = SystemClock.elapsedRealtime();
if (isExpanded && isVisible) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index eb46d53b5157..7c0e0b0983fb 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -80,6 +80,7 @@ public class PreferencesHelper implements RankingConfig {
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_ID = "id";
+ private static final String ATT_APP_OVERLAY = "overlay";
private static final String ATT_PRIORITY = "priority";
private static final String ATT_VISIBILITY = "visibility";
private static final String ATT_IMPORTANCE = "importance";
@@ -92,6 +93,7 @@ public class PreferencesHelper implements RankingConfig {
private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
private static final boolean DEFAULT_SHOW_BADGE = true;
+ private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
/**
* Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
* fields.
@@ -104,6 +106,7 @@ public class PreferencesHelper implements RankingConfig {
@IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
public @interface LockableAppFields {
int USER_LOCKED_IMPORTANCE = 0x00000001;
+ int USER_LOCKED_APP_OVERLAY = 0x00000002;
}
// pkg|uid => PackagePreferences
@@ -169,7 +172,9 @@ public class PreferencesHelper implements RankingConfig {
XmlUtils.readIntAttribute(
parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
+ XmlUtils.readBooleanAttribute(
+ parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
r.importance = XmlUtils.readIntAttribute(
parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
r.priority = XmlUtils.readIntAttribute(
@@ -264,11 +269,12 @@ public class PreferencesHelper implements RankingConfig {
private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
return getOrCreatePackagePreferences(pkg, uid,
- DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
+ DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
+ DEFAULT_ALLOW_APP_OVERLAY);
}
private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
- int priority, int visibility, boolean showBadge) {
+ int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
final String key = packagePreferencesKey(pkg, uid);
synchronized (mPackagePreferences) {
PackagePreferences
@@ -282,6 +288,7 @@ public class PreferencesHelper implements RankingConfig {
r.priority = priority;
r.visibility = visibility;
r.showBadge = showBadge;
+ r.appOverlay = allowAppOverlay;
try {
createDefaultChannelIfNeeded(r);
@@ -382,7 +389,8 @@ public class PreferencesHelper implements RankingConfig {
|| r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
|| r.channels.size() > 0
|| r.groups.size() > 0
- || r.delegate != null;
+ || r.delegate != null
+ || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
if (hasNonDefaultSettings) {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
@@ -395,6 +403,9 @@ public class PreferencesHelper implements RankingConfig {
if (r.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
}
+ if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
+ out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+ }
out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
Integer.toString(r.lockedAppFields));
@@ -439,6 +450,20 @@ public class PreferencesHelper implements RankingConfig {
out.endTag(null, TAG_RANKING);
}
+ public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+ PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
+ p.appOverlay = allowed;
+ p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+ }
+
+ public boolean areAppOverlaysAllowed(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+ }
+
+ public int getAppLockedFields(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).lockedAppFields;
+ }
+
/**
* Gets importance.
*/
@@ -512,7 +537,6 @@ public class PreferencesHelper implements RankingConfig {
// apps can't update the blocked status or app overlay permission
if (fromTargetApp) {
group.setBlocked(oldGroup.isBlocked());
- group.setAllowAppOverlay(oldGroup.canOverlayApps());
group.unlockFields(group.getUserLockedFields());
group.lockFields(oldGroup.getUserLockedFields());
} else {
@@ -521,9 +545,6 @@ public class PreferencesHelper implements RankingConfig {
group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
updateChannelsBypassingDnd(mContext.getUserId());
}
- if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
- group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
- }
}
}
r.groups.put(group.getId(), group);
@@ -1581,6 +1602,7 @@ public class PreferencesHelper implements RankingConfig {
int priority = DEFAULT_PRIORITY;
int visibility = DEFAULT_VISIBILITY;
boolean showBadge = DEFAULT_SHOW_BADGE;
+ boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Delegate delegate = null;
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 6d5982708f4c..16143d3ae9e0 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -60,7 +60,6 @@ class IdmapManager {
boolean createIdmap(@NonNull final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
if (DEBUG) {
Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
+ overlayPackage.packageName);
@@ -70,16 +69,19 @@ class IdmapManager {
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+ if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+ return true;
+ }
+ return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
} else {
mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
@@ -88,15 +90,15 @@ class IdmapManager {
}
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+ return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
} else {
mInstaller.removeIdmap(oi.baseCodePath);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d4719049e426..a7f114655dcb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -223,8 +223,8 @@ public final class OverlayManagerService extends SystemService {
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
- mSettingsFile =
- new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
+ mSettingsFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
mPackageManager = new PackageManagerHelper();
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(installer);
@@ -717,9 +717,9 @@ public final class OverlayManagerService extends SystemService {
final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
List<String> list = new ArrayList<>();
if (!"android".equals(targetPackageName)) {
@@ -730,8 +730,8 @@ public final class OverlayManagerService extends SystemService {
}
}
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
if (DEBUG) {
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -789,7 +789,7 @@ public final class OverlayManagerService extends SystemService {
if (!mSettingsFile.getBaseFile().exists()) {
return;
}
- try (final FileInputStream stream = mSettingsFile.openRead()) {
+ try (FileInputStream stream = mSettingsFile.openRead()) {
mSettings.restore(stream);
// We might have data for dying users if the device was
@@ -915,8 +915,8 @@ public final class OverlayManagerService extends SystemService {
if (!verbose) {
int count = 0;
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
count += mCache.get(userId).size();
}
@@ -929,8 +929,8 @@ public final class OverlayManagerService extends SystemService {
return;
}
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
pw.println(TAB1 + "User " + userId);
final HashMap<String, PackageInfo> map = mCache.get(userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 112059daf95e..b0d2704196a6 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -55,8 +55,8 @@ import java.util.Set;
*/
final class OverlayManagerServiceImpl {
// Flags to use in conjunction with updateState.
- private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
- private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+ private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+ private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
private final PackageManagerHelper mPackageManager;
private final IdmapManager mIdmapManager;
@@ -89,8 +89,8 @@ final class OverlayManagerServiceImpl {
// a change in priority is only relevant for static RROs: specifically,
// a regular RRO should not have its state reset only because a change
// in priority
- if (theTruth.isStaticOverlayPackage() &&
- theTruth.overlayPriority != oldSettings.priority) {
+ if (theTruth.isStaticOverlayPackage()
+ && theTruth.overlayPriority != oldSettings.priority) {
return true;
}
return false;
@@ -294,8 +294,8 @@ final class OverlayManagerServiceImpl {
final int userId, final int flags) {
boolean modified = false;
final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
- final int N = ois.size();
- for (int i = 0; i < N; i++) {
+ final int n = ois.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = ois.get(i);
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
userId);
@@ -610,8 +610,8 @@ final class OverlayManagerServiceImpl {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
final List<String> paths = new ArrayList<>(overlays.size());
- final int N = overlays.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlays.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlays.get(i);
if (oi.isEnabled()) {
paths.add(oi.packageName);
@@ -632,8 +632,8 @@ final class OverlayManagerServiceImpl {
userId);
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
- if (targetPackage != null && overlayPackage != null &&
- !("android".equals(targetPackageName)
+ if (targetPackage != null && overlayPackage != null
+ && !("android".equals(targetPackageName)
&& overlayPackage.isStaticOverlayPackage())) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
@@ -663,7 +663,7 @@ final class OverlayManagerServiceImpl {
private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
@Nullable final PackageInfo overlayPackage, final int userId, final int flags)
- throws OverlayManagerSettings.BadKeyException {
+ throws OverlayManagerSettings.BadKeyException {
if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
return STATE_TARGET_UPGRADING;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 572d36897040..ee067460cc45 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -290,21 +290,22 @@ final class OverlayManagerSettings {
return;
}
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
pw.println(item.mPackageName + ":" + item.getUserId() + " {");
pw.increaseIndent();
- pw.print("mPackageName.......: "); pw.println(item.mPackageName);
- pw.print("mUserId............: "); pw.println(item.getUserId());
- pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName());
- pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath());
- pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState()));
- pw.print("mIsEnabled.........: "); pw.println(item.isEnabled());
- pw.print("mIsStatic..........: "); pw.println(item.isStatic());
- pw.print("mPriority..........: "); pw.println(item.mPriority);
- pw.print("mCategory..........: "); pw.println(item.mCategory);
+ pw.println("mPackageName.......: " + item.mPackageName);
+ pw.println("mUserId............: " + item.getUserId());
+ pw.println("mTargetPackageName.: " + item.getTargetPackageName());
+ pw.println("mBaseCodePath......: " + item.getBaseCodePath());
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mIsEnabled.........: " + item.isEnabled());
+ pw.println("mIsStatic..........: " + item.isStatic());
+ pw.println("mPriority..........: " + item.mPriority);
+ pw.println("mCategory..........: " + item.mCategory);
pw.decreaseIndent();
pw.println("}");
@@ -400,8 +401,8 @@ final class OverlayManagerSettings {
xml.startTag(null, TAG_OVERLAYS);
XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
- final int N = table.size();
- for (int i = 0; i < N; i++) {
+ final int n = table.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = table.get(i);
persistRow(xml, item);
}
@@ -542,8 +543,8 @@ final class OverlayManagerSettings {
}
private int select(@NonNull final String packageName, final int userId) {
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
return i;
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index d576d330c4a8..e2b1cba3819f 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -121,8 +121,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
for (final String targetPackageName : allOverlays.keySet()) {
out.println(targetPackageName);
List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
- final int N = overlaysForTarget.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlaysForTarget.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlaysForTarget.get(i);
String status;
switch (oi.state) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 093b85e5ed4c..f9e31aed1174 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -402,12 +402,17 @@ public class PackageDexOptimizer {
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
- // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
- // in isolation (and avoid to extract/verify the main apk if it's in the class path).
- // Note this trades correctness for performance since the resulting slow down is
- // unacceptable in some cases until b/64530081 is fixed.
- String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ String classLoaderContext;
+ if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
+ // If we have an unknown (not yet set), or a variable class loader chain, compile
+ // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
+ // this might lead to a incorrect compilation.
+ // TODO(calin): We should just extract in this case.
+ classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ } else {
+ classLoaderContext = dexUseInfo.getClassLoaderContext();
+ }
+
int reason = options.getCompilationReason();
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 206a88bde616..0d0db9457672 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -250,6 +250,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private int mParentSessionId;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionApplied;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionReady;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionFailed;
+ @GuardedBy("mLock")
+ private int mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+
/**
* Path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
@@ -470,6 +479,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (info.childSessionIds == null) {
info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
}
+ info.isSessionApplied = mStagedSessionApplied;
+ info.isSessionReady = mStagedSessionReady;
+ info.isSessionFailed = mStagedSessionFailed;
+ info.setStagedSessionErrorCode(mStagedSessionErrorCode);
}
return info;
}
@@ -1051,6 +1064,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (isStaged()) {
// STOPSHIP: implement staged sessions
+ mStagedSessionReady = true;
dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
return;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 28fb01d6d958..6453db5db1b6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -888,7 +888,7 @@ public class PackageManagerService extends IPackageManager.Stub
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
- private volatile boolean mWebInstantAppsDisabled;
+ private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -5904,8 +5904,8 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Returns whether or not instant apps have been disabled remotely.
*/
- private boolean areWebInstantAppsDisabled() {
- return mWebInstantAppsDisabled;
+ private boolean areWebInstantAppsDisabled(int userId) {
+ return mWebInstantAppsDisabled.get(userId);
}
private boolean isInstantAppResolutionAllowed(
@@ -5936,7 +5936,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
- } else if (areWebInstantAppsDisabled()) {
+ } else if (areWebInstantAppsDisabled(userId)) {
return false;
}
}
@@ -6816,7 +6816,7 @@ public class PackageManagerService extends IPackageManager.Stub
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
- final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
+ final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// remove locally resolved instant app web results when disabled
@@ -20124,16 +20124,21 @@ public class PackageManagerService extends IPackageManager.Stub
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mWebInstantAppsDisabled =
- (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
- (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+ boolean ephemeralFeatureDisabled =
+ Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ boolean instantAppsDisabledForUser =
+ ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
+ Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
+ mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
+ }
}
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
- false, co, UserHandle.USER_SYSTEM);
+ false, co, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
- .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 37a35a25ac1e..357872ef3321 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -630,9 +630,13 @@ class PackageManagerShellCommand extends ShellCommand {
pw.print("=");
}
pw.print(info.packageName);
- if (showVersionCode && !isApex) {
+ if (showVersionCode) {
pw.print(" versionCode:");
- pw.print(info.applicationInfo.versionCode);
+ if (info.applicationInfo != null) {
+ pw.print(info.applicationInfo.versionCode);
+ } else {
+ pw.print(info.versionCode);
+ }
}
if (listInstaller && !isApex) {
pw.print(" installer=");
@@ -2779,7 +2783,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" Prints all system libraries.");
pw.println("");
pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
- pw.println(" [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
+ pw.println(" [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
@@ -2792,6 +2796,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -l: ignored (used for compatibility with older releases)");
pw.println(" -U: also show the package UID");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" --show-versioncode: also show the version code");
pw.println(" --apex-only: only show APEX packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 519a20d51ddb..e68c2386e03b 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -16,9 +16,9 @@
package com.android.server.pm.dex;
+import android.os.Build;
import android.util.AtomicFile;
import android.util.Slog;
-import android.os.Build;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -26,26 +26,27 @@ import com.android.internal.util.FastPrintWriter;
import com.android.server.pm.AbstractStatsBase;
import com.android.server.pm.PackageManagerServiceUtils;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
-import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
-
/**
* Stat file which store usage information about dex files.
*/
@@ -86,6 +87,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
"=UnsupportedClassLoaderContext=";
+ /**
+ * Limit on how many secondary DEX paths we store for a single owner, to avoid one app causing
+ * unbounded memory consumption.
+ */
+ @VisibleForTesting
+ /* package */ static final int MAX_SECONDARY_FILES_PER_OWNER = 100;
+
// Map which structures the information we have on a package.
// Maps package name to package data (which stores info about UsedByOtherApps and
// secondary dex files.).
@@ -164,8 +172,12 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
if (existingData == null) {
// It's the first time we see this dex file.
- packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
- return true;
+ if (packageUseInfo.mDexUseInfoMap.size() < MAX_SECONDARY_FILES_PER_OWNER) {
+ packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+ return true;
+ } else {
+ return updateLoadingPackages;
+ }
} else {
if (ownerUserId != existingData.mOwnerUserId) {
// Oups, this should never happen, the DexManager who calls this should
@@ -863,15 +875,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
public String getClassLoaderContext() { return mClassLoaderContext; }
- @VisibleForTesting
- /* package */ boolean isUnknownClassLoaderContext() {
+ public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
- @VisibleForTesting
- /* package */ boolean isVariableClassLoaderContext() {
+ public boolean isVariableClassLoaderContext() {
return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 51619cf940a8..164af38be5a6 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -143,6 +143,13 @@ public final class DefaultPermissionGrantPolicy {
LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
+ private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
+ static {
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+ }
+
private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
static {
ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
@@ -690,7 +697,7 @@ public final class DefaultPermissionGrantPolicy {
// Companion devices
grantSystemFixedPermissionsToSystemPackage(
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
- LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS);
// Ringtone Picker
grantSystemFixedPermissionsToSystemPackage(
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6e4c00eed181..fc21adbdcca3 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -718,12 +718,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
disabledData += " }";
final UiState state = getUiState(displayId);
- Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+ Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1
+ ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+ ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
}
final UiState state = getUiState(displayId);
- if (state.disableEquals(net1, net2)) {
+ if (!state.disableEquals(net1, net2)) {
state.setDisabled(net1, net2);
mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
@@ -1254,12 +1254,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onNotificationSmartRepliesAdded(String key, int replyCount)
- throws RemoteException {
+ public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+ int smartActionCount, boolean generatedByAssistant) {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+ mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
+ smartActionCount, generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 64ff9cf21ba0..f00877030145 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -60,6 +60,7 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
@@ -85,6 +86,7 @@ import android.system.Os;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
import android.view.IWindowManager;
@@ -120,12 +122,13 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
- static final String TAG = "WallpaperManagerService";
- static final boolean DEBUG = false;
- static final boolean DEBUG_LIVE = DEBUG || true;
+ private static final String TAG = "WallpaperManagerService";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_LIVE = true;
public static class Lifecycle extends SystemService {
private IWallpaperManagerService mService;
@@ -163,14 +166,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- final Object mLock = new Object();
+ private final Object mLock = new Object();
/**
* Minimum time between crashes of a wallpaper service for us to consider
* restarting it vs. just reverting to the static wallpaper.
*/
- static final long MIN_WALLPAPER_CRASH_TIME = 10000;
- static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
+ private static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+ private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
static final String WALLPAPER = "wallpaper_orig";
static final String WALLPAPER_CROP = "wallpaper";
static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
@@ -178,7 +181,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
static final String WALLPAPER_INFO = "wallpaper_info.xml";
// All the various per-user state files we need to be aware of
- static final String[] sPerUserFiles = new String[] {
+ private static final String[] sPerUserFiles = new String[] {
WALLPAPER, WALLPAPER_CROP,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
WALLPAPER_INFO
@@ -335,7 +338,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- void notifyLockWallpaperChanged() {
+ private void notifyLockWallpaperChanged() {
final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) {
try {
@@ -487,7 +490,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean success = false;
// Only generate crop for default display.
- final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+ final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
@@ -640,11 +643,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- final Context mContext;
- final IWindowManager mIWindowManager;
- final IPackageManager mIPackageManager;
- final MyPackageMonitor mMonitor;
- final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final IWindowManager mIWindowManager;
+ private final IPackageManager mIPackageManager;
+ private final MyPackageMonitor mMonitor;
+ private final AppOpsManager mAppOpsManager;
private final DisplayManager mDisplayManager;
private final DisplayManager.DisplayListener mDisplayListener =
@@ -654,11 +657,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void onDisplayAdded(int displayId) {
synchronized (mLock) {
if (mLastWallpaper != null) {
- final WallpaperConnection.DisplayConnector connector =
- mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
- if (connector == null) return;
-
- connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ if (supportsMultiDisplay(mLastWallpaper.connection)) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ return;
+ }
+ // System wallpaper does not support multiple displays, attach this display to
+ // the fallback wallpaper.
+ if (mFallbackWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
+ .connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
+ } else {
+ Slog.w(TAG, "No wallpaper can be added to the new display");
+ }
}
}
}
@@ -667,12 +682,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void onDisplayRemoved(int displayId) {
synchronized (mLock) {
if (mLastWallpaper != null) {
- final WallpaperConnection.DisplayConnector connector =
- mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ WallpaperData targetWallpaper = null;
+ if (mLastWallpaper.connection.containsDisplay(displayId)) {
+ targetWallpaper = mLastWallpaper;
+ } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ targetWallpaper = mFallbackWallpaper;
+ }
+ if (targetWallpaper == null) return;
+ WallpaperConnection.DisplayConnector connector =
+ targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
if (connector == null) return;
connector.disconnectLocked();
- mLastWallpaper.connection.removeDisplayConnector(displayId);
- mLastWallpaper.removeDisplayData(displayId);
+ targetWallpaper.connection.removeDisplayConnector(displayId);
+ removeDisplayData(displayId);
}
}
}
@@ -686,35 +708,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* Map of color listeners per user id.
* The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
*/
- final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
- WallpaperData mLastWallpaper;
- IWallpaperManagerCallback mKeyguardListener;
- boolean mWaitingForUnlock;
- boolean mShuttingDown;
+ private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+ mColorsChangedListeners;
+ private WallpaperData mLastWallpaper;
+ private IWallpaperManagerCallback mKeyguardListener;
+ private boolean mWaitingForUnlock;
+ private boolean mShuttingDown;
/**
* ID of the current wallpaper, changed every time anything sets a wallpaper.
* This is used for external detection of wallpaper update activity.
*/
- int mWallpaperId;
+ private int mWallpaperId;
/**
* Name of the component used to display bitmap wallpapers from either the gallery or
* built-in wallpapers.
*/
- final ComponentName mImageWallpaper;
+ private final ComponentName mImageWallpaper;
/**
* Name of the default wallpaper component; might be different from mImageWallpaper
*/
- final ComponentName mDefaultWallpaperComponent;
+ private final ComponentName mDefaultWallpaperComponent;
- final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
- final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
+ private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+ private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
- final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
- int mCurrentUserId = UserHandle.USER_NULL;
- boolean mInAmbientMode;
+ private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
+
+ private WallpaperData mFallbackWallpaper;
+
+ private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mInAmbientMode;
static class WallpaperData {
@@ -780,18 +807,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
= new RemoteCallbackList<IWallpaperManagerCallback>();
- private static final class DisplayData {
- int mWidth = -1;
- int mHeight = -1;
- final Rect mPadding = new Rect(0, 0, 0, 0);
- final int mDisplayId;
-
- DisplayData(int displayId) {
- mDisplayId = displayId;
- }
- }
- private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
-
/**
* The crop hint supplied for displaying a subset of the source image
*/
@@ -812,24 +827,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean sourceExists() {
return wallpaperFile.exists();
}
+ }
- void removeDisplayData(int displayId) {
- mDisplayDatas.remove(displayId);
+ private static final class DisplayData {
+ int mWidth = -1;
+ int mHeight = -1;
+ final Rect mPadding = new Rect(0, 0, 0, 0);
+ final int mDisplayId;
+
+ DisplayData(int displayId) {
+ mDisplayId = displayId;
}
}
- private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
- WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+ private void removeDisplayData(int displayId) {
+ mDisplayDatas.remove(displayId);
+ }
+
+ private DisplayData getDisplayDataOrCreate(int displayId) {
+ DisplayData wpdData = mDisplayDatas.get(displayId);
if (wpdData == null) {
- wpdData = new WallpaperData.DisplayData(displayId);
+ wpdData = new DisplayData(displayId);
ensureSaneWallpaperDisplaySize(wpdData, displayId);
- data.mDisplayDatas.append(displayId, wpdData);
+ mDisplayDatas.append(displayId, wpdData);
}
return wpdData;
}
- private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
- int displayId) {
+ private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
// We always want to have some reasonable width hint.
final int baseSize = getMaximumSizeDimension(displayId);
if (wpdData.mWidth < baseSize) {
@@ -842,12 +867,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private int getMaximumSizeDimension(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
+ display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ }
return display.getMaximumSizeDimension();
}
- void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
- for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
- final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+ void forEachDisplayData(Consumer<DisplayData> action) {
+ for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
+ final DisplayData wpdData = mDisplayDatas.valueAt(i);
action.accept(wpdData);
}
}
@@ -859,6 +888,43 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return mWallpaperId;
}
+ private boolean supportsMultiDisplay(WallpaperConnection connection) {
+ if (connection != null) {
+ return connection.mInfo == null // This is image wallpaper
+ || connection.mInfo.supportsMultipleDisplays();
+ }
+ return false;
+ }
+
+ private void updateFallbackConnection() {
+ if (mLastWallpaper == null || mFallbackWallpaper == null) return;
+ final WallpaperConnection systemConnection = mLastWallpaper.connection;
+ final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+ if (fallbackConnection == null) {
+ Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
+ return;
+ }
+ if (supportsMultiDisplay(systemConnection)
+ && fallbackConnection.mDisplayConnector.size() != 0) {
+ fallbackConnection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine != null) {
+ connector.disconnectLocked();
+ }
+ });
+ fallbackConnection.mDisplayConnector.clear();
+ } else {
+ fallbackConnection.appendConnectorWithCondition(display ->
+ fallbackConnection.isUsableDisplay(display)
+ && display.getDisplayId() != DEFAULT_DISPLAY
+ && !fallbackConnection.containsDisplay(display.getDisplayId()));
+ fallbackConnection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine == null) {
+ connector.connectLocked(fallbackConnection, mFallbackWallpaper);
+ }
+ });
+ }
+ }
+
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
@@ -877,8 +943,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
void ensureStatusHandled() {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
- mDisplayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
if (mDimensionsChanged) {
try {
mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
@@ -898,6 +963,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+ if (connection.mService == null) {
+ Slog.w(TAG, "WallpaperService is not connected yet");
+ return;
+ }
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
try {
mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
@@ -906,15 +975,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- mDisplayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
wpdData.mWidth, wpdData.mHeight,
wpdData.mPadding, mDisplayId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
- if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ if (wallpaper != null && !wallpaper.wallpaperUpdating
&& connection.getConnectedEngineSize() == 0) {
bindWallpaperComponentLocked(null /* componentName */, false /* force */,
false /* fromUser */, wallpaper, null /* reply */);
@@ -982,19 +1050,36 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private void initDisplayState() {
+ // Do not initialize fallback wallpaper
+ if (!mWallpaper.equals(mFallbackWallpaper)) {
+ if (supportsMultiDisplay(this)) {
+ // The system wallpaper is image wallpaper or it can supports multiple displays.
+ appendConnectorWithCondition(this::isUsableDisplay);
+ } else {
+ // The system wallpaper does not support multiple displays, so just attach it on
+ // default display.
+ mDisplayConnector.append(DEFAULT_DISPLAY,
+ new DisplayConnector(DEFAULT_DISPLAY));
+ }
+ }
+ }
+
+ private void appendConnectorWithCondition(Predicate<Display> tester) {
final Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
- if (isUsableDisplay(display)) {
+ if (tester.test(display)) {
final int displayId = display.getDisplayId();
- mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+ final DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector == null) {
+ mDisplayConnector.append(displayId,
+ new DisplayConnector(displayId));
+ }
}
}
}
- // TODO(b/115486823) Support the system decorations change at runtime.
private boolean isUsableDisplay(Display display) {
return display != null && display.hasAccess(mClientUid)
- // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
&& (display.supportsSystemDecorations()
|| display.getDisplayId() == DEFAULT_DISPLAY);
}
@@ -1027,6 +1112,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return connector;
}
+ boolean containsDisplay(int displayId) {
+ return mDisplayConnector.get(displayId) != null;
+ }
+
void removeDisplayConnector(int displayId) {
final DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector != null) {
@@ -1044,7 +1133,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// when we have an engine, but I'm not sure about
// locking there and anyway we always need to be able to
// recover if there is something wrong.
- saveSettingsLocked(mWallpaper.userId);
+ if (!mWallpaper.equals(mFallbackWallpaper)) {
+ saveSettingsLocked(mWallpaper.userId);
+ }
FgThread.getHandler().removeCallbacks(mResetRunnable);
}
}
@@ -1533,8 +1624,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// This corrects for mislabeling bugs that might have arisen from move-to
// operations involving the wallpaper files. This isn't timing-critical,
// so we do it in the background to avoid holding up the user unlock operation.
- if (mUserRestorecon.get(userId) != Boolean.TRUE) {
- mUserRestorecon.put(userId, Boolean.TRUE);
+ if (!mUserRestorecon.get(userId)) {
+ mUserRestorecon.put(userId, true);
Runnable relabeler = new Runnable() {
@Override
public void run() {
@@ -1562,7 +1653,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
for (String filename : sPerUserFiles) {
new File(wallpaperDir, filename).delete();
}
- mUserRestorecon.remove(userId);
+ mUserRestorecon.delete(userId);
}
}
@@ -1789,7 +1880,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
throw new IllegalArgumentException("Cannot find display with id=" + displayId);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
if (width != wpdData.mWidth || height != wpdData.mHeight) {
wpdData.mWidth = width;
wpdData.mHeight = height;
@@ -1826,8 +1917,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
return wpdData.mWidth;
} else {
return 0;
@@ -1845,8 +1935,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
return wpdData.mHeight;
} else {
return 0;
@@ -1872,7 +1961,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
throw new IllegalArgumentException("padding must be positive: " + padding);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
if (!padding.equals(wpdData.mPadding)) {
wpdData.mPadding.set(padding);
if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
@@ -1940,8 +2029,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return null;
}
// Only for default display.
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
if (outParams != null) {
outParams.putInt("width", wpdData.mWidth);
@@ -2173,14 +2261,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// We know a-priori that there is no lock-only wallpaper currently
WallpaperData lockWP = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
- final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
- DEFAULT_DISPLAY);
- final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
- DEFAULT_DISPLAY);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
- lockWPDData.mWidth = sysWPDData.mWidth;
- lockWPDData.mHeight = sysWPDData.mHeight;
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
@@ -2320,7 +2402,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return false;
}
- boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+ private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
if (DEBUG_LIVE) {
Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
@@ -2443,15 +2525,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, msg);
return false;
}
- if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+ if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
+ && !wallpaper.equals(mFallbackWallpaper)) {
detachWallpaperLocked(mLastWallpaper);
}
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
- if (wallpaper.userId == mCurrentUserId) {
+ if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
mLastWallpaper = wallpaper;
}
+ updateFallbackConnection();
} catch (RemoteException e) {
String msg = "Remote exception for " + componentName + "\n" + e;
if (fromUser) {
@@ -2463,7 +2547,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return true;
}
- void detachWallpaperLocked(WallpaperData wallpaper) {
+ private void detachWallpaperLocked(WallpaperData wallpaper) {
if (wallpaper.connection != null) {
if (wallpaper.connection.mReply != null) {
try {
@@ -2473,7 +2557,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.connection.mReply = null;
}
mContext.unbindService(wallpaper.connection);
- wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
+ wallpaper.connection.forEachDisplayConnector(
+ WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
wallpaper.connection = null;
@@ -2481,12 +2566,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+ private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
wallpaper.wallpaperComponent = null;
detachWallpaperLocked(wallpaper);
}
- void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+ private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
@@ -2596,8 +2681,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
out.attribute(null, "width", Integer.toString(wpdData.mWidth));
@@ -2755,10 +2839,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
}
}
+ initializeFallbackWallpaper();
}
boolean success = false;
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2845,8 +2929,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private void initializeFallbackWallpaper() {
+ if (mFallbackWallpaper == null) {
+ if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
+ mFallbackWallpaper = new WallpaperData(
+ UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+ mFallbackWallpaper.allowBackup = false;
+ mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+ }
+ }
+
private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
- final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData size = getDisplayDataOrCreate(displayId);
if (displayId == DEFAULT_DISPLAY) {
// crop, if not previously specified
@@ -2869,7 +2964,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
- final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+ final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
if (!keepDimensionHints) {
wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
@@ -2963,7 +3058,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// Restore the named resource bitmap to both source + crop files
- boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+ private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
String resName = wallpaper.name.substring(4);
@@ -3048,8 +3143,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
for (int i = 0; i < mWallpaperMap.size(); i++) {
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
- pw.print(": id="); pw.println(wallpaper.wallpaperId);
- forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
+ pw.println(" Display state:");
+ forEachDisplayData(wpSize -> {
pw.print(" displayId=");
pw.println(wpSize.mDisplayId);
pw.print(" mWidth=");
@@ -3072,11 +3168,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.println(conn.mInfo.getComponent());
}
conn.forEachDisplayConnector(connector -> {
- pw.print(" mDisplayId=");
+ pw.print(" mDisplayId=");
pw.println(connector.mDisplayId);
- pw.print(" mToken=");
+ pw.print(" mToken=");
pw.println(connector.mToken);
- pw.print(" mEngine=");
+ pw.print(" mEngine=");
pw.println(connector.mEngine);
});
pw.print(" mService=");
@@ -3090,18 +3186,38 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
pw.print(": id="); pw.println(wallpaper.wallpaperId);
- forEachDisplayData(wallpaper, wpSize -> {
- pw.print(" displayId=");
- pw.println(wpSize.mDisplayId);
- pw.print(" mWidth="); pw.print(wpSize.mWidth);
- pw.print(" mHeight="); pw.println(wpSize.mHeight);
- pw.print(" mPadding="); pw.println(wpSize.mPadding);
- });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
}
-
+ pw.println("Fallback wallpaper state:");
+ pw.print(" User "); pw.print(mFallbackWallpaper.userId);
+ pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
+ pw.print(" mCropHint="); pw.println(mFallbackWallpaper.cropHint);
+ pw.print(" mName="); pw.println(mFallbackWallpaper.name);
+ pw.print(" mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
+ if (mFallbackWallpaper.connection != null) {
+ WallpaperConnection conn = mFallbackWallpaper.connection;
+ pw.print(" Fallback Wallpaper connection ");
+ pw.print(conn);
+ pw.println(":");
+ if (conn.mInfo != null) {
+ pw.print(" mInfo.component=");
+ pw.println(conn.mInfo.getComponent());
+ }
+ conn.forEachDisplayConnector(connector -> {
+ pw.print(" mDisplayId=");
+ pw.println(connector.mDisplayId);
+ pw.print(" mToken=");
+ pw.println(connector.mToken);
+ pw.print(" mEngine=");
+ pw.println(connector.mEngine);
+ });
+ pw.print(" mService=");
+ pw.println(conn.mService);
+ pw.print(" mLastDiedTime=");
+ pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5f00bcc26984..4d8440a899a3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,6 +42,7 @@ import static android.app.WindowConfiguration.activityTypeToString;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -1178,7 +1179,8 @@ final class ActivityRecord extends ConfigurationContainer {
private boolean isHomeIntent(Intent intent) {
return ACTION_MAIN.equals(intent.getAction())
- && intent.hasCategory(CATEGORY_HOME)
+ && (intent.hasCategory(CATEGORY_HOME)
+ || intent.hasCategory(CATEGORY_SECONDARY_HOME))
&& intent.getCategories().size() == 1
&& intent.getData() == null
&& intent.getType() == null;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2663d997162c..6755c7363ede 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4914,7 +4914,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Update override configurations of all tasks in the stack.
final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
- final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
mTmpBounds.clear();
mTmpInsetBounds.clear();
@@ -4922,7 +4921,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- task.updateOverrideConfiguration(taskBounds, insetBounds);
+ task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds);
}
if (task.hasDisplayedBounds()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 986115726efb..f662d0cc7f23 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5475,6 +5475,31 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return intent;
}
+ /**
+ * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
+ * activities.
+ *
+ * @param preferredPackage Specify a preferred package name, otherwise use secondary home
+ * component defined in config_secondaryHomeComponent.
+ * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
+ */
+ Intent getSecondaryHomeIntent(String preferredPackage) {
+ final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+ if (preferredPackage == null) {
+ // Using the component stored in config if no package name.
+ final String secondaryHomeComponent = mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+ } else {
+ intent.setPackage(preferredPackage);
+ }
+ intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
+ if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ intent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
+ }
+ return intent;
+ }
+
ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
if (info == null) return null;
ApplicationInfo newInfo = new ApplicationInfo(info);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8d49bf374baf..d8b2b5200f0c 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1837,23 +1837,23 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return false;
}
}
+ }
- if (transferStartingWindow(transferFrom)) {
- return true;
- }
-
- // There is no existing starting window, and we don't want to create a splash screen, so
- // that's it!
- if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- return false;
- }
+ if (transferStartingWindow(transferFrom)) {
+ return true;
+ }
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- startingData = new SplashScreenStartingData(mWmService, pkg,
- theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- getMergedOverrideConfiguration());
- scheduleAddStartingWindow();
+ // There is no existing starting window, and we don't want to create a splash screen, so
+ // that's it!
+ if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ return false;
}
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+ startingData = new SplashScreenStartingData(mWmService, pkg,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ getMergedOverrideConfiguration());
+ scheduleAddStartingWindow();
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8b8cadc7a4a6..6d3c69385a09 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -505,23 +505,6 @@ public class DisplayPolicy {
// TODO: Make it can take screenshot on external display
mScreenshotHelper = displayContent.isDefaultDisplay
? new ScreenshotHelper(mContext) : null;
- }
-
- void systemReady() {
- mSystemGestures.systemReady();
- }
-
- private int getDisplayId() {
- return mDisplayContent.getDisplayId();
- }
-
- void onDisplayRemoved() {
- mDisplayContent.unregisterPointerEventListener(mSystemGestures);
- }
-
- void configure(int width, int height, int shortSizeDp) {
- // Allow the navigation bar to move on non-square small devices (phones).
- mNavigationBarCanMove = width != height && shortSizeDp < 600;
if (mDisplayContent.isDefaultDisplay) {
mHasStatusBar = true;
@@ -541,6 +524,23 @@ public class DisplayPolicy {
}
}
+ void systemReady() {
+ mSystemGestures.systemReady();
+ }
+
+ private int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
+ void onDisplayRemoved() {
+ mDisplayContent.unregisterPointerEventListener(mSystemGestures);
+ }
+
+ void configure(int width, int height, int shortSizeDp) {
+ // Allow the navigation bar to move on non-square small devices (phones).
+ mNavigationBarCanMove = width != height && shortSizeDp < 600;
+ }
+
void updateConfigurationDependentBehaviors() {
mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f9c9d33c561a..639ed02a1e48 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,6 +1,5 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -10,6 +9,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,37 +204,6 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
- /** Callback to get pointer display id. */
- @Override
- public int getPointerDisplayId() {
- synchronized (mService.mGlobalLock) {
- // If desktop mode is not enabled, show on the default display.
- if (!mService.mForceDesktopModeOnExternalDisplays) {
- return DEFAULT_DISPLAY;
- }
-
- // Look for the topmost freeform display.
- int firstExternalDisplayId = DEFAULT_DISPLAY;
- for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
- // Heuristic solution here. Currently when "Freeform windows" developer option is
- // enabled we automatically put secondary displays in freeform mode and emulating
- // "desktop mode". It also makes sense to show the pointer on the same display.
- if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return displayContent.getDisplayId();
- }
-
- if (firstExternalDisplayId == DEFAULT_DISPLAY
- && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
- firstExternalDisplayId = displayContent.getDisplayId();
- }
- }
-
- // Look for the topmost non-default display
- return firstExternalDisplayId;
- }
- }
-
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index fff42c5d0a5f..8dda48523c83 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -2,6 +2,6 @@ ogunwale@google.com
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index ec2d6737ee51..cb9cbd6465fb 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -134,10 +134,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mWindowManager.deferSurfaceLayout();
try {
- final int userId = mService.getCurrentUserId();
-
// Kick off the assist data request in the background before showing the target activity
- requestAssistData(recentsComponent, recentsUid, assistDataReceiver, userId);
+ requestAssistData(recentsComponent, recentsUid, assistDataReceiver);
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
@@ -164,7 +162,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
.setCallingUid(recentsUid)
.setCallingPackage(recentsComponent.getPackageName())
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
- .setMayWait(userId)
+ .setMayWait(mService.getCurrentUserId())
.execute();
// Move the recents activity into place for the animation
@@ -221,7 +219,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
* Requests assist data for the top visible activities.
*/
private void requestAssistData(ComponentName recentsComponent, int recentsUid,
- @Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
+ @Deprecated IAssistDataReceiver assistDataReceiver) {
final AppOpsManager appOpsManager = (AppOpsManager)
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
final List<IBinder> topActivities =
@@ -237,8 +235,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
final ContentCaptureManagerInternal imService =
LocalServices.getService(ContentCaptureManagerInternal.class);
final IBinder activityToken = topActivities.get(activityIndex);
- if (imService == null
- || !imService.sendActivityAssistData(userId, activityToken, data)) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null && (imService == null
+ || !imService.sendActivityAssistData(r.mUserId, activityToken, data))) {
// Otherwise, use the provided assist data receiver
super.onAssistDataReceivedLocked(data, activityIndex, activityCount);
}
@@ -263,7 +262,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
int activityCount) {
// Try to notify the intelligence service
final IBinder activityToken = topActivities.get(activityIndex);
- imService.sendActivityAssistData(userId, activityToken, data);
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null) {
+ imService.sendActivityAssistData(r.mUserId, activityToken, data);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d0144fdf670a..8ec97c5117f1 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -90,17 +90,18 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.power.V1_0.PowerHint;
-import android.os.Build;
import android.os.FactoryTest;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.IntArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -110,6 +111,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ResolverActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
@@ -346,35 +348,53 @@ class RootActivityContainer extends ConfigurationContainer
}
/**
- * This starts home activity on displays that can have system decorations and only if the
- * home activity can have multiple instances.
+ * This starts home activity on displays that can have system decorations based on displayId -
+ * Default display always use primary home component.
+ * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves
+ * according to the priorities listed below.
+ * - If default home is not set, always use the secondary home defined in the config.
+ * - Use currently selected primary home activity.
+ * - Use the activity in the same package as currently selected primary home activity.
+ * If there are multiple activities matched, use first one.
+ * - Use the secondary home defined in the config.
*/
boolean startHomeOnDisplay(int userId, String reason, int displayId) {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ Intent homeIntent;
+ ActivityInfo aInfo;
+ if (displayId == DEFAULT_DISPLAY) {
+ homeIntent = mService.getHomeIntent();
+ aInfo = resolveHomeActivity(userId, homeIntent);
+ } else {
+ Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);
+ aInfo = info.first;
+ homeIntent = info.second;
+ }
if (aInfo == null) {
return false;
}
- if (!canStartHomeOnDisplay(aInfo, displayId,
- false /* allowInstrumenting */)) {
+ if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
return false;
}
+ // Updates the home component of the intent.
+ homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+ homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
// Update the reason for ANR debugging to verify if the user activity is the one that
// actually launched.
final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
- aInfo.applicationInfo.uid);
+ aInfo.applicationInfo.uid) + ":" + displayId;
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
displayId);
return true;
}
/**
- * This resolves the home activity info and updates the home component of the given intent.
+ * This resolves the home activity info.
* @return the home activity info if any.
*/
- private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+ @VisibleForTesting
+ ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
final int flags = ActivityManagerService.STOCK_PM_FLAGS;
final ComponentName comp = homeIntent.getComponent();
ActivityInfo aInfo = null;
@@ -400,13 +420,82 @@ class RootActivityContainer extends ConfigurationContainer
return null;
}
- homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
- homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
return aInfo;
}
+ @VisibleForTesting
+ Pair<ActivityInfo, Intent> resolveSecondaryHomeActivity(int userId, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException(
+ "resolveSecondaryHomeActivity: Should not be DEFAULT_DISPLAY");
+ }
+ // Resolve activities in the same package as currently selected primary home activity.
+ Intent homeIntent = mService.getHomeIntent();
+ ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ if (aInfo != null) {
+ if (ResolverActivity.class.getName().equals(aInfo.name)) {
+ // Always fallback to secondary home component if default home is not set.
+ aInfo = null;
+ } else {
+ // Look for secondary home activities in the currently selected default home
+ // package.
+ homeIntent = mService.getSecondaryHomeIntent(aInfo.applicationInfo.packageName);
+ final List<ResolveInfo> resolutions = resolveActivities(userId, homeIntent);
+ final int size = resolutions.size();
+ final String targetName = aInfo.name;
+ aInfo = null;
+ for (int i = 0; i < size; i++) {
+ ResolveInfo resolveInfo = resolutions.get(i);
+ // We need to traverse all resolutions to check if the currently selected
+ // default home activity is present.
+ if (resolveInfo.activityInfo.name.equals(targetName)) {
+ aInfo = resolveInfo.activityInfo;
+ break;
+ }
+ }
+ if (aInfo == null && size > 0) {
+ // First one is the best.
+ aInfo = resolutions.get(0).activityInfo;
+ }
+ }
+ }
+
+ if (aInfo != null) {
+ if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
+ aInfo = null;
+ }
+ }
+
+ // Fallback to secondary home component.
+ if (aInfo == null) {
+ homeIntent = mService.getSecondaryHomeIntent(null);
+ aInfo = resolveHomeActivity(userId, homeIntent);
+ }
+ return Pair.create(aInfo, homeIntent);
+ }
+
+ /**
+ * Retrieve all activities that match the given intent.
+ * The list should already ordered from best to worst matched.
+ * {@link android.content.pm.PackageManager#queryIntentActivities}
+ */
+ @VisibleForTesting
+ List<ResolveInfo> resolveActivities(int userId, Intent homeIntent) {
+ List<ResolveInfo> resolutions;
+ try {
+ final String resolvedType =
+ homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+ resolutions = AppGlobals.getPackageManager().queryIntentActivities(homeIntent,
+ resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId).getList();
+
+ } catch (RemoteException e) {
+ resolutions = new ArrayList<>();
+ }
+ return resolutions;
+ }
+
boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
@@ -457,6 +546,14 @@ class RootActivityContainer extends ConfigurationContainer
return true;
}
+ final boolean deviceProvisioned = Settings.Global.getInt(
+ mService.mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY && !deviceProvisioned) {
+ // Can't launch home on secondary display before device is provisioned.
+ return false;
+ }
+
final ActivityDisplay display = getActivityDisplay(displayId);
if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
// Can't launch home on display that doesn't support system decorations.
@@ -464,13 +561,9 @@ class RootActivityContainer extends ConfigurationContainer
}
final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
- && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
- && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+ && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
if (!supportMultipleInstance) {
- // Can't launch home on other displays if it requested to be single instance. Also we
- // don't allow home applications that target before Q to have multiple home activity
- // instances because they may not be expected to have multiple home scenario and
- // haven't explicitly request for single instance.
+ // Can't launch home on secondary displays if it requested to be single instance.
return false;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 801e5f2038ad..dcade2f012db 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -794,8 +794,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
private void handleResizingWindows() {
for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mWmService.mResizingWindows.get(i);
- if (win.mAppFreezing) {
- // Don't remove this window until rotation has completed.
+ if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
+ // Don't remove this window until rotation has completed and is not waiting for the
+ // complete configuration.
continue;
}
win.reportResized();
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 5bb64407a28d..8c800097e49d 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1716,7 +1716,7 @@ class TaskRecord extends ConfigurationContainer {
updateTaskDescription();
}
- private void adjustForMinimalTaskDimensions(Rect bounds) {
+ private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
if (bounds == null) {
return;
}
@@ -1747,9 +1747,8 @@ class TaskRecord extends ConfigurationContainer {
return;
}
- final Rect configBounds = getRequestedOverrideBounds();
if (adjustWidth) {
- if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
+ if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
bounds.left = bounds.right - minWidth;
} else {
// Either left bounds match, or neither match, or the previous bounds were
@@ -1758,7 +1757,7 @@ class TaskRecord extends ConfigurationContainer {
}
}
if (adjustHeight) {
- if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
+ if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
bounds.top = bounds.bottom - minHeight;
} else {
// Either top bounds match, or neither match, or the previous bounds were
@@ -2113,11 +2112,15 @@ class TaskRecord extends ConfigurationContainer {
// TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
Configuration overrideConfig) {
+ // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
+ // changes left bound vs. right bound, or top bound vs. bottom bound.
+ mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
+
inOutConfig.setTo(overrideConfig);
Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
- adjustForMinimalTaskDimensions(outOverrideBounds);
+ adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bf83ca912eed..43d2dcf7e0d1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,7 +107,6 @@ static struct {
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
- jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -175,6 +174,15 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
+static void updatePointerControllerFromViewport(
+ sp<PointerController> controller, const DisplayViewport* const viewport) {
+ if (controller != nullptr && viewport != nullptr) {
+ const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+ const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+ controller->setDisplayViewport(width, height, viewport->orientation);
+ }
+}
+
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -234,7 +242,6 @@ public:
jfloatArray matrixArr);
virtual TouchAffineTransformation getTouchAffineTransformation(
const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
- virtual void updatePointerDisplay();
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -307,11 +314,10 @@ private:
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked();
+ void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
- const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
- int32_t getPointerDisplayId();
+
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -385,10 +391,9 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c
return false;
}
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
- REQUIRES(mLock) {
- for (const DisplayViewport& v : mLocked.viewports) {
- if (v.displayId == displayId) {
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+ for (const DisplayViewport& v : viewports) {
+ if (v.type == ViewportType::VIEWPORT_INTERNAL) {
return &v;
}
}
@@ -415,10 +420,20 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
}
}
- { // acquire lock
+ const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
+ {
AutoMutex _l(mLock);
+ const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+ // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+ // if they are different.
+ const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+ (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+ if (internalViewportChanged) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ updatePointerControllerFromViewport(controller, newInternalViewport);
+ }
mLocked.viewports = viewports;
- } // release lock
+ }
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -541,41 +556,13 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
- updateInactivityTimeoutLocked();
- }
- return controller;
-}
+ const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+ updatePointerControllerFromViewport(controller, internalViewport);
-int32_t NativeInputManager::getPointerDisplayId() {
- JNIEnv* env = jniEnv();
- jint pointerDisplayId = env->CallIntMethod(mServiceObj,
- gServiceClassInfo.getPointerDisplayId);
- if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
- pointerDisplayId = ADISPLAY_ID_DEFAULT;
- }
-
- return pointerDisplayId;
-}
-
-void NativeInputManager::updatePointerDisplay() {
- ATRACE_CALL();
-
- jint pointerDisplayId = getPointerDisplayId();
-
- AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
- if (viewport == nullptr) {
- ALOGW("Can't find pointer display viewport, fallback to default display.");
- viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
- }
-
- if (viewport != nullptr) {
- controller->setDisplayViewport(*viewport);
- }
+ updateInactivityTimeoutLocked(controller);
}
+ return controller;
}
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
@@ -834,16 +821,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
- updateInactivityTimeoutLocked();
- }
-}
-void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller == nullptr) {
- return;
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ updateInactivityTimeoutLocked(controller);
+ }
}
+}
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+ REQUIRES(mLock) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1837,9 +1824,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
- GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
- "getPointerDisplayId", "()I");
-
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cf03d613634f..046c9912dee3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2116,30 +2116,33 @@ public final class SystemServer {
private void startContentCaptureService(@NonNull Context context) {
- // First check if it was explicitly enabled by Settings
- boolean explicitlySupported = false;
+ // Check if it was explicitly enabled by Settings
final String settings = Settings.Global.getString(context.getContentResolver(),
Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED);
- if (settings != null) {
- explicitlySupported = Boolean.parseBoolean(settings);
- if (explicitlySupported) {
- Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
- } else {
- Slog.d(TAG, "ContentCaptureService explicitly disabled by Settings");
- return;
- }
+ if (settings == null) {
+ // Better be safe than sorry...
+ Slog.d(TAG, "ContentCaptureService disabled because its not set by OEM");
+ return;
}
-
- // Then check if OEM overlaid the resource that defines the service.
- if (!explicitlySupported) {
- final String serviceName = context
- .getString(com.android.internal.R.string.config_defaultContentCaptureService);
- if (TextUtils.isEmpty(serviceName)) {
- Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
+ switch (settings) {
+ case "always":
+ // Should be used only during development
+ Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
+ break;
+ case "default":
+ // Default case: check if OEM overlaid the resource that defines the service.
+ final String serviceName = context.getString(
+ com.android.internal.R.string.config_defaultContentCaptureService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
+ return;
+ }
+ break;
+ default:
+ // Kill switch for OEMs
+ Slog.d(TAG, "ContentCaptureService disabled because its set to: " + settings);
return;
- }
}
-
traceBeginAndSlog("StartContentCaptureService");
mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS);
traceEnd();
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index cee6fa96bbc5..35d29e75c0e4 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -39,7 +39,6 @@ import android.annotation.Nullable;
import android.net.MacAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -85,7 +84,7 @@ public class DhcpServer {
@NonNull
private final ServerHandler mHandler;
@NonNull
- private final InterfaceParams mIface;
+ private final String mIfName;
@NonNull
private final DhcpLeaseRepository mLeaseRepo;
@NonNull
@@ -161,20 +160,20 @@ public class DhcpServer {
}
}
- public DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+ public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(looper, iface, params, log, null);
+ this(looper, ifName, params, log, null);
}
@VisibleForTesting
- DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+ DhcpServer(@NonNull Looper looper, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log,
@Nullable Dependencies deps) {
if (deps == null) {
deps = new DependenciesImpl();
}
mHandler = new ServerHandler(looper);
- mIface = iface;
+ mIfName = ifName;
mServingParams = params;
mLog = log;
mDeps = deps;
@@ -444,7 +443,7 @@ public class DhcpServer {
private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
try {
- mDeps.addArpEntry(inetAddr, macAddr, mIface.name, mSocket);
+ mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
return true;
} catch (IOException e) {
mLog.e("Error adding client to ARP table", e);
@@ -526,7 +525,7 @@ public class DhcpServer {
// SO_BINDTODEVICE actually takes a string. This works because the first member
// of struct ifreq is a NULL-terminated interface name.
// TODO: add a setsockoptString()
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIface.name);
+ Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
NetworkUtils.protectFromVpn(mSocket);
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 823c0a1ac7b0..493350d776f3 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -138,9 +138,9 @@ public class IpServer extends StateMachine {
return NetdService.getInstance();
}
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ public DhcpServer makeDhcpServer(Looper looper, String ifName,
DhcpServingParams params, SharedLog log) {
- return new DhcpServer(looper, iface, params, log);
+ return new DhcpServer(looper, ifName, params, log);
}
}
@@ -256,12 +256,6 @@ public class IpServer extends StateMachine {
if (mUsingLegacyDhcp) {
return true;
}
-
- final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName);
- if (ifaceParams == null) {
- Log.e(TAG, "Failed to find interface params for DHCPv4");
- return false;
- }
final DhcpServingParams params;
try {
params = new DhcpServingParams.Builder()
@@ -277,7 +271,7 @@ public class IpServer extends StateMachine {
return false;
}
- mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params,
+ mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
mLog.forSubComponent("DHCP"));
mDhcpServer.start();
return true;
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 0c9c85acf6e2..9159f0d47621 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -41,7 +41,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE := FrameworksServicesRoboTests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, backup/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res
@@ -81,4 +82,13 @@ LOCAL_JAVA_LIBRARIES := \
LOCAL_TEST_PACKAGE := FrameworksServicesLib
-include external/robolectric-shadows/run_robotests.mk \ No newline at end of file
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
+ $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+
+include external/robolectric-shadows/run_robotests.mk
+
+###################################################################
+# include subdir Android.mk files
+###################################################################
+include $(CLEAR_VARS)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
new file mode 100644
index 000000000000..cc59b0c9bb16
--- /dev/null
+++ b/services/robotests/backup/Android.mk
@@ -0,0 +1,84 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+###################################################################
+# BackupFrameworksServicesLib app just for Robolectric test target #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ bmgrlib \
+ bu \
+ services.backup \
+ services.core \
+ services.net
+
+include $(BUILD_PACKAGE)
+
+###################################################################
+# BackupFrameworksServicesLib Robolectric test target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := BackupFrameworksServicesRoboTests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../src/com/android/server/testing/shadows)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_JAVA_RESOURCE_DIRS := config
+
+# Include the testing libraries
+LOCAL_JAVA_LIBRARIES := \
+ platform-test-annotations \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt \
+ testng
+
+LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+###################################################################
+# BackupFrameworksServicesLib runner target to run the previous target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunBackupFrameworksServicesRoboTests
+
+LOCAL_JAVA_LIBRARIES := \
+ BackupFrameworksServicesRoboTests \
+ platform-test-annotations \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt \
+ testng
+
+LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib
+
+include external/robolectric-shadows/run_robotests.mk
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
new file mode 100644
index 000000000000..850557a9b693
--- /dev/null
+++ b/services/robotests/backup/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK \ No newline at end of file
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
index 099cde04bd5e..099cde04bd5e 100644
--- a/services/robotests/src/android/app/backup/BackupUtilsTest.java
+++ b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
index 4ff5b7c03a8f..4ff5b7c03a8f 100644
--- a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java
+++ b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
index 1705f5b8c704..1705f5b8c704 100644
--- a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
+++ b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
diff --git a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
index 6869f5612c1d..6869f5612c1d 100644
--- a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java
+++ b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
index 5b226f36d565..5b226f36d565 100644
--- a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
index affa1f3f97f3..affa1f3f97f3 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 000000000000..b253e0ae0a40
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,1536 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+
+import org.junit.After;
+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;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
+@Presubmit
+public class BackupManagerServiceTest {
+ private static final String TEST_PACKAGE = "package";
+ private static final String TEST_TRANSPORT = "transport";
+ private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
+
+ private ShadowContextWrapper mShadowContext;
+ private Context mContext;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+ @Mock private UserBackupManagerService mUserOneService;
+ @Mock private UserBackupManagerService mUserTwoService;
+
+ /** Initialize {@link BackupManagerService}. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Application application = RuntimeEnvironment.application;
+ mContext = application;
+ mShadowContext = shadowOf(application);
+
+ mUserOneId = UserHandle.USER_SYSTEM + 1;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ /**
+ * Clean up and reset state that was created for testing {@link BackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ ShadowBinder.reset();
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+ * specifically to prevent overloading the logs in production.
+ */
+ @Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
+ /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+ @Test
+ public void testConstructor_doesNotRegisterUsers() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullContext_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ /* context */ null,
+ new Trampoline(mContext),
+ startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullTrampoline_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, /* trampoline */ null, startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullBackupThread_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, new Trampoline(mContext), /* backupThread */ null));
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.getServiceForUserIfCallerHasPermission(
+ mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller does
+ * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ // ---------------------------------------------
+ // Backup agent tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ // ---------------------------------------------
+ // Transport tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
+
+ verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
+
+ verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransport(mUserOneId);
+
+ verify(mUserOneService).getCurrentTransport();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransport(mUserTwoId);
+
+ verify(mUserOneService, never()).getCurrentTransport();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransportComponent(mUserOneId);
+
+ verify(mUserOneService).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransportComponent(mUserTwoId);
+
+ verify(mUserOneService, never()).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransports(mUserOneId);
+
+ verify(mUserOneService).listAllTransports();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransports(mUserTwoId);
+
+ verify(mUserOneService, never()).listAllTransports();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransportComponents(mUserOneId);
+
+ verify(mUserOneService).listAllTransportComponents();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransportComponents(mUserTwoId);
+
+ verify(mUserOneService, never()).listAllTransportComponents();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ mUserOneId,
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService)
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ mUserTwoId,
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService, never())
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ mUserOneId, transport.getTransportComponent(), callback);
+
+ verify(mUserOneService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ mUserTwoId, transport.getTransportComponent(), callback);
+
+ verify(mUserOneService, never())
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ // ---------------------------------------------
+ // Settings tests
+ // ---------------------------------------------
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserTwoService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserOneId, true);
+
+ verify(mUserOneService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setAutoRestore(mUserOneId, true);
+
+ verify(mUserOneService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setAutoRestore(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserOneId);
+
+ verify(mUserOneService).isBackupEnabled();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserOneService, never()).isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // Backup tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
+
+ verify(mUserOneService).filterAppsEligibleForBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
+
+ verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserTwoService).backupNow();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserOneId);
+
+ verify(mUserOneService).backupNow();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserOneService, never()).backupNow();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.requestBackup(
+ mUserTwoId, packages, observer, monitor, 0));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserTwoService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserOneId);
+
+ verify(mUserOneService).cancelBackups();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserOneService, never()).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(job);
+
+ verify(mUserOneService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(job);
+
+ verify(mUserOneService, never()).beginFullBackup(job);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService).endFullBackup();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService, never()).endFullBackup();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(mUserOneId, packages);
+
+ verify(mUserOneService).fullTransportBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(mUserTwoId, packages);
+
+ verify(mUserOneService, never()).fullTransportBackup(packages);
+ }
+
+ // ---------------------------------------------
+ // Restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ // ---------------------------------------------
+ // Adb backup/restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService).hasBackupPassword();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService, never()).hasBackupPassword();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ /* parcelFileDescriptor*/ null,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserTwoService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserOneId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService, never())
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserTwoService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
+
+ verify(mUserOneService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ mUserOneId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+
+ verify(mUserOneService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ mUserTwoId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+
+ verify(mUserOneService, never())
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ // ---------------------------------------------
+ // Service tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+ }
+
+ private BackupManagerService createService() {
+ return new BackupManagerService(
+ mContext, new Trampoline(mContext), startBackupThread(null));
+ }
+
+ private BackupManagerService createServiceAndRegisterUser(
+ int userId, UserBackupManagerService userBackupManagerService) {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.startServiceForUser(userId, userBackupManagerService);
+ return backupManagerService;
+ }
+
+ /**
+ * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+ * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+ * permission.
+ */
+ private void setCallerAndGrantInteractUserPermission(
+ @UserIdInt int userId, boolean shouldGrantPermission) {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+ if (shouldGrantPermission) {
+ mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+ } else {
+ mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+ }
+ }
+
+ private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209e1f50..8e17209e1f50 100644
--- a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index 693092da0334..693092da0334 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index efbcb960c1e9..1e468d44b8c7 100644
--- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -87,6 +87,7 @@ public class UserBackupManagerServiceTest {
private static final String TAG = "BMSTest";
private static final String PACKAGE_1 = "some.package.1";
private static final String PACKAGE_2 = "some.package.2";
+ private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
private HandlerThread mBackupThread;
@@ -979,6 +980,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1000,6 +1002,7 @@ public class UserBackupManagerServiceTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1022,6 +1025,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
/* context */ null,
new Trampoline(mContext),
mBackupThread,
@@ -1041,6 +1045,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
/* trampoline */ null,
mBackupThread,
@@ -1060,6 +1065,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
/* backupThread */ null,
@@ -1079,6 +1085,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1089,8 +1096,8 @@ public class UserBackupManagerServiceTest {
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
- * File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+ * File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1098,6 +1105,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1108,8 +1116,8 @@ public class UserBackupManagerServiceTest {
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
- * File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+ * File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1117,6 +1125,7 @@ public class UserBackupManagerServiceTest {
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1127,7 +1136,7 @@ public class UserBackupManagerServiceTest {
private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
- mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+ USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
}
private void setUpPowerManager(UserBackupManagerService backupManagerService) {
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
index 3f57240bc0e9..3f57240bc0e9 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
index 4354db72554a..4354db72554a 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
index 17c9a86169be..17c9a86169be 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
index 0bf14174e5c3..0bf14174e5c3 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
index d0e5fb335da9..d0e5fb335da9 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
index 2bbbf2857146..2bbbf2857146 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
index 8e801a133909..8e801a133909 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
index 2f872beacd17..2f872beacd17 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
index 978bddb7301a..978bddb7301a 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
index 19ef8fb339ba..19ef8fb339ba 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
index 77b734785424..77b734785424 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
index 936b5dca033d..936b5dca033d 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
index 549437454e9c..549437454e9c 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
index 277dc372e73c..277dc372e73c 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
index 729580cf5101..729580cf5101 100644
--- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
index 5342efa18a97..5342efa18a97 100644
--- a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
index 89977f82c145..89977f82c145 100644
--- a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
index 48216f8d7aca..48216f8d7aca 100644
--- a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
index 49bb410ceb65..49bb410ceb65 100644
--- a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
index 87f21bfa59c2..87f21bfa59c2 100644
--- a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
index 319ec89f445e..319ec89f445e 100644
--- a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 423512ce4b70..423512ce4b70 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index a14cc51a3ab6..a14cc51a3ab6 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
index b00b922ed3d0..b00b922ed3d0 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
index d5603d64687a..d5603d64687a 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index a1b8a9520524..a1b8a9520524 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 099127cbeb4b..cf51e19edb00 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -169,6 +169,7 @@ public class KeyValueBackupTaskTest {
private static final PackageData PACKAGE_2 = keyValuePackage(2);
private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
"android.app.backup.BackupAgent$SharedPrefsSynchronizer";
+ private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
@Mock private DataChangedJournal mOldJournal;
@@ -224,7 +225,7 @@ public class KeyValueBackupTaskTest {
setUpBinderCallerAndApplicationAsSystem(mApplication);
mBackupManagerService =
spy(createUserBackupManagerServiceAndRunTasks(
- mContext, mBaseStateDir, mDataDir, mTransportManager));
+ USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
mApplication,
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
index 3698b79872b1..3698b79872b1 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
index 5ac26f49abdc..5ac26f49abdc 100644
--- a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
index 7ec2a4e66903..7ec2a4e66903 100644
--- a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
index b9a77fbafe61..b9a77fbafe61 100644
--- a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
index 38a54dab3fb8..38a54dab3fb8 100644
--- a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 859392d80d36..859392d80d36 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 06f6d21b9ca9..b9785707fe96 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -58,13 +58,17 @@ public class BackupManagerServiceTestUtils {
* <p>If the class-under-test is going to execute methods as the system, it's a good idea to
* also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
*
- * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+ * @see #createUserBackupManagerServiceAndRunTasks(int, Context, HandlerThread, File, File,
* TransportManager)
*/
public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
- Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+ int userId,
+ Context context,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
return createUserBackupManagerServiceAndRunTasks(
- context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+ userId, context, startBackupThread(null), baseStateDir, dataDir, transportManager);
}
/**
@@ -75,6 +79,7 @@ public class BackupManagerServiceTestUtils {
* also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
*/
public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
+ int userId,
Context context,
HandlerThread backupThread,
File baseStateDir,
@@ -82,6 +87,7 @@ public class BackupManagerServiceTestUtils {
TransportManager transportManager) {
UserBackupManagerService backupManagerService =
UserBackupManagerService.createAndInitializeService(
+ userId,
context,
new Trampoline(context),
backupThread,
diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
index 0428796f51fa..0428796f51fa 100644
--- a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
index f9177a80fbfe..f9177a80fbfe 100644
--- a/services/robotests/src/com/android/server/backup/testing/PackageData.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
index 3fe1f3f90f2f..3fe1f3f90f2f 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
index 77f5d9a48c18..77f5d9a48c18 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportData.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
index f6ed6307c82f..f6ed6307c82f 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java
index b0e00a2656d0..b0e00a2656d0 100644
--- a/services/robotests/src/com/android/server/backup/testing/Utils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
index 7dd0d927edd0..7dd0d927edd0 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
index 7281a3c87a29..7281a3c87a29 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
index f01a6b067c8a..f01a6b067c8a 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index 58bce1cdfbf1..000000000000
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup;
-
-import static com.android.server.backup.testing.TransportData.backupTransport;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
-import com.android.server.backup.testing.TransportData;
-import com.android.server.testing.shadows.ShadowBinder;
-
-import org.junit.After;
-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;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
-@Presubmit
-public class BackupManagerServiceTest {
- private static final String TEST_PACKAGE = "package";
- private static final String TEST_TRANSPORT = "transport";
-
- private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
-
- private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
-
- private ShadowContextWrapper mShadowContext;
- @Mock private UserBackupManagerService mUserBackupManagerService;
- private BackupManagerService mBackupManagerService;
- private Context mContext;
- @UserIdInt private int mUserId;
-
- /** Initialize {@link BackupManagerService}. */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Application application = RuntimeEnvironment.application;
- mContext = application;
- mShadowContext = shadowOf(application);
- mUserId = NON_USER_SYSTEM;
- mBackupManagerService =
- new BackupManagerService(
- application,
- new Trampoline(application),
- BackupManagerServiceTestUtils.startBackupThread(null));
- mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
- }
-
- /**
- * Clean up and reset state that was created for testing {@link BackupManagerService}
- * operations.
- */
- @After
- public void tearDown() throws Exception {
- ShadowBinder.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
- * This is specifically to prevent overloading the logs in production.
- */
- @Test
- public void testMoreDebug_isFalse() throws Exception {
- boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
- assertThat(moreDebug).isFalse();
- }
-
- // TODO(b/118520567): Change the following tests to use the per-user instance of
- // UserBackupManagerService once it's implemented. Currently these tests only test the straight
- // forward redirection.
-
- // ---------------------------------------------
- // Backup agent tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDataChanged_callsDataChangedForUser() throws Exception {
- mBackupManagerService.dataChanged(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
- IBinder agentBinder = mock(IBinder.class);
-
- mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
-
- verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
- mBackupManagerService.agentDisconnected(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testOpComplete_callsOpCompleteForUser() throws Exception {
- mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
-
- verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
- }
-
- // ---------------------------------------------
- // Transport tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
- String[] transports = {TEST_TRANSPORT};
-
- mBackupManagerService.initializeTransports(transports, /* observer */ null);
-
- verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
- mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
-
- verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
- mBackupManagerService.getCurrentTransport();
-
- verify(mUserBackupManagerService).getCurrentTransport();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
- throws Exception {
- mBackupManagerService.getCurrentTransportComponent();
-
- verify(mUserBackupManagerService).getCurrentTransportComponent();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
- mBackupManagerService.listAllTransports();
-
- verify(mUserBackupManagerService).listAllTransports();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
- throws Exception {
- mBackupManagerService.listAllTransportComponents();
-
- verify(mUserBackupManagerService).listAllTransportComponents();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
- mBackupManagerService.getTransportWhitelist();
-
- verify(mUserBackupManagerService).getTransportWhitelist();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
- throws Exception {
- TransportData transport = backupTransport();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- mBackupManagerService.updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mUserBackupManagerService)
- .updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
- mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
- TransportData transport = backupTransport();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- mBackupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
-
- verify(mUserBackupManagerService)
- .selectBackupTransportAsync(transport.getTransportComponent(), callback);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
- mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
- mBackupManagerService.getDestinationString(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
- mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
- mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
- }
-
- // ---------------------------------------------
- // Settings tests
- // ---------------------------------------------
- /**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void setBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.setBackupEnabled(mUserId, true));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
- * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
- * the same as the target user id.
- */
- @Test
- public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.setBackupEnabled(mUserId, true);
-
- verify(mUserBackupManagerService).setBackupEnabled(true);
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.setBackupEnabled(mUserId, true);
-
- verify(mUserBackupManagerService).setBackupEnabled(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
- mBackupManagerService.setAutoRestore(true);
-
- verify(mUserBackupManagerService).setAutoRestore(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
- mBackupManagerService.setBackupProvisioned(true);
-
- verify(mUserBackupManagerService).setBackupProvisioned(true);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.isBackupEnabled(mUserId));
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.isBackupEnabled(mUserId);
-
- verify(mUserBackupManagerService).isBackupEnabled();
- }
-
- // ---------------------------------------------
- // Backup tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
- mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
- throws Exception {
- String[] packages = {TEST_PACKAGE};
-
- mBackupManagerService.filterAppsEligibleForBackup(packages);
-
- verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testBackupNow_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.backupNow(mUserId));
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBackupNow_callsBackupNowForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.backupNow(mUserId);
-
- verify(mUserBackupManagerService).backupNow();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
- * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testRequestBackup_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRequestBackup_callsRequestBackupForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
- /* flags */ 0);
-
- verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testCancelBackups_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.cancelBackups(mUserId));
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.cancelBackups(mUserId);
-
- verify(mUserBackupManagerService).cancelBackups();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
- FullBackupJob job = new FullBackupJob();
-
- mBackupManagerService.beginFullBackup(job);
-
- verify(mUserBackupManagerService).beginFullBackup(job);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
- mBackupManagerService.endFullBackup();
-
- verify(mUserBackupManagerService).endFullBackup();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
- String[] packages = {TEST_PACKAGE};
-
- mBackupManagerService.fullTransportBackup(packages);
-
- verify(mUserBackupManagerService).fullTransportBackup(packages);
- }
-
- // ---------------------------------------------
- // Restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
- mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
-
- verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
- mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
- throws Exception {
- mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
- }
-
- // ---------------------------------------------
- // Adb backup/restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
- mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
-
- verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
- mBackupManagerService.hasBackupPassword();
-
- verify(mUserBackupManagerService).hasBackupPassword();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
- * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testAdbBackup_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(SecurityException.class,
- () ->
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- /* parcelFileDescriptor*/ null,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- null));
-
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
- * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} does not require
- * the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is the
- * same as the target user id.
- */
- @Test
- public void testAdbBackup_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserBackupManagerService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbBackup_callsAdbBackupForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserBackupManagerService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} throws
- * a {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL
- * permission.
- */
- @Test
- public void testAdbRestore_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(SecurityException.class,
- () -> mBackupManagerService.adbRestore(mUserId, null));
-
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} does
- * not require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id
- * is the same as the target user id.
- */
- @Test
- public void testAdbRestore_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
- verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
- verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
- throws Exception {
- IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
- mBackupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
-
- verify(mUserBackupManagerService)
- .acknowledgeAdbBackupOrRestore(
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
- }
-
- // ---------------------------------------------
- // Service tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDump_callsDumpForUser() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
-
- mBackupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
- }
-
- private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 3979a8e762d3..148faada6381 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -42,7 +41,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
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.eq;
@@ -66,7 +64,6 @@ import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -96,6 +93,8 @@ public class AlarmManagerServiceTest {
@Mock
private ContentResolver mMockResolver;
@Mock
+ private Context mMockContext;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -221,17 +220,16 @@ public class AlarmManagerServiceTest {
.thenReturn(STANDBY_BUCKET_ACTIVE);
doReturn(Looper.getMainLooper()).when(Looper::myLooper);
- final Context context = spy(InstrumentationRegistry.getTargetContext());
- when(context.getContentResolver()).thenReturn(mMockResolver);
- doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any());
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
doReturn("min_futurity=0").when(() ->
Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
- mInjector = new Injector(context);
- mService = new AlarmManagerService(context, mInjector);
+ mInjector = new Injector(mMockContext);
+ mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
doNothing().when(mService).publishBinderService(any(), any());
mService.onStart();
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ spyOn(mService.mHandler);
assertEquals(0, mService.mConstants.MIN_FUTURITY);
assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
@@ -273,7 +271,7 @@ public class AlarmManagerServiceTest {
final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
- verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class),
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
verify(mWakeLock).acquire();
onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
@@ -423,11 +421,23 @@ public class AlarmManagerServiceTest {
assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
- verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(),
any(Handler.class), isNull(), any());
assertNull(restrictedAlarms.get(TEST_CALLING_UID));
}
+ @Test
+ public void sendsTimeTickOnInteractive() {
+ final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ // Stubbing so the handler doesn't actually run the runnable.
+ doReturn(true).when(mService.mHandler).post(runnableCaptor.capture());
+ // change interactive state: false -> true
+ mService.interactiveStateChangedLocked(false);
+ mService.interactiveStateChangedLocked(true);
+ runnableCaptor.getValue().run();
+ verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL);
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cf4d3a8070f9..1b5ba263e6dd 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -25,7 +25,6 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index d965f8a34fa4..0fd59216fa21 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -41,11 +41,14 @@ import java.util.Map;
/**
* Tests for {@link SettingsToPropertiesMapper}
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:SettingsToPropertiesMapperTest
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsToPropertiesMapperTest {
- private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+ private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
private static final String[] TEST_MAPPING = new String[] {
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
};
@@ -77,7 +80,28 @@ public class SettingsToPropertiesMapperTest {
}
if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
Assert.fail(globalSetting + " contains invalid characters. "
- + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+ + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
+ }
+ }
+ }
+
+ @Test
+ public void validateRegisteredDeviceConfigScopes() {
+ HashSet<String> hashSet = new HashSet<>();
+ for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) {
+ if (hashSet.contains(deviceConfigScope)) {
+ Assert.fail("deviceConfigScope "
+ + deviceConfigScope
+ + " is registered more than once in "
+ + "SettingsToPropertiesMapper.sDeviceConfigScopes.");
+ }
+ hashSet.add(deviceConfigScope);
+ if (TextUtils.isEmpty(deviceConfigScope)) {
+ Assert.fail("empty deviceConfigScope registered.");
+ }
+ if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) {
+ Assert.fail(deviceConfigScope + " contains invalid characters. "
+ + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
}
}
}
@@ -98,8 +122,7 @@ public class SettingsToPropertiesMapperTest {
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- systemPropertyName,
- true);
+ systemPropertyName);
propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
Assert.assertEquals("testValue2", propValue);
@@ -107,8 +130,7 @@ public class SettingsToPropertiesMapperTest {
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- systemPropertyName,
- true);
+ systemPropertyName);
propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
Assert.assertEquals("", propValue);
}
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
new file mode 100644
index 000000000000..52f434db3be3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appops;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpNotedListener;
+import android.content.Context;
+import android.os.Process;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/**
+ * Tests watching noted ops.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppOpsNotedWatcherTest {
+
+ private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
+
+ public void testWatchNotedOpsRequiresPermission() {
+ // Create a mock listener
+ final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+ // Try to start watching noted ops
+ final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ try {
+ appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+ AppOpsManager.OPSTR_RECORD_AUDIO}, listener);
+ fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
+ } catch (SecurityException expected) {
+ /*ignored*/
+ }
+ }
+
+ @Test
+ public void testWatchNotedOps() {
+ // Create a mock listener
+ final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+ // Start watching noted ops
+ final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+ AppOpsManager.OPSTR_CAMERA}, listener);
+
+ // Note some ops
+ appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(),
+ getContext().getPackageName());
+ appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(),
+ getContext().getPackageName());
+
+ // Verify that we got called for the ops being noted
+ final InOrder inOrder = inOrder(listener);
+ inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+ .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+ eq(Process.myUid()), eq(getContext().getPackageName()),
+ eq(AppOpsManager.MODE_ALLOWED));
+ inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+ .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA),
+ eq(Process.myUid()), eq(getContext().getPackageName()),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ // Stop watching
+ appOpsManager.stopWatchingNoted(listener);
+
+ // This should be the only two callbacks we got
+ verifyNoMoreInteractions(listener);
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index d7a398e50a66..0851cf3bc4c0 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -132,19 +132,21 @@ public class TrampolineTest {
}
@Test
- public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+ public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
}
@Test
- public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+ public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock).startServiceForUser(10);
}
@@ -300,10 +302,22 @@ public class TrampolineTest {
}
@Test
+ public void dataChangedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
+ }
+
+ @Test
public void dataChanged_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.dataChanged(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
}
@Test
@@ -313,10 +327,22 @@ public class TrampolineTest {
}
@Test
+ public void clearBackupDataForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+ }
+
+ @Test
public void clearBackupData_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
}
@Test
@@ -326,10 +352,22 @@ public class TrampolineTest {
}
@Test
+ public void agentConnectedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
+
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
+ }
+
+ @Test
public void agentConnected_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
- verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
}
@Test
@@ -339,10 +377,22 @@ public class TrampolineTest {
}
@Test
+ public void agentDisconnectedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
+ }
+
+ @Test
public void agentDisconnected_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.agentDisconnected(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
}
@Test
@@ -352,10 +402,22 @@ public class TrampolineTest {
}
@Test
+ public void restoreAtInstallForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
+
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
+ }
+
+ @Test
public void restoreAtInstall_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
- verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
}
@Test
@@ -390,10 +452,22 @@ public class TrampolineTest {
}
@Test
+ public void setAutoRestoreForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.setAutoRestoreForUser(mUserId, true);
+
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
+ }
+
+ @Test
public void setAutoRestore_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.setAutoRestore(true);
- verify(mBackupManagerServiceMock).setAutoRestore(true);
+
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
}
@Test
@@ -406,7 +480,7 @@ public class TrampolineTest {
public void setBackupProvisioned_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
mTrampoline.setBackupProvisioned(true);
- verify(mBackupManagerServiceMock).setBackupProvisioned(true);
+ verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
@@ -505,15 +579,17 @@ public class TrampolineTest {
@Test
public void fullTransportBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.fullTransportBackup(PACKAGE_NAMES);
+ mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void fullTransportBackup_forwarded() throws RemoteException {
+ public void fullTransportBackupForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.fullTransportBackup(PACKAGE_NAMES);
- verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
+
+ mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
+
+ verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
}
@Test
@@ -538,12 +614,43 @@ public class TrampolineTest {
}
@Test
+ public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.acknowledgeFullBackupOrRestoreForUser(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
+
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
+ }
+
+ @Test
public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
mFullBackupRestoreObserverMock);
- verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
}
@Test
@@ -553,13 +660,22 @@ public class TrampolineTest {
}
@Test
- public void getCurrentTransport_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
+ public void getCurrentTransportForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
+ }
+
+ @Test
+ public void getCurrentTransport_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
- verify(mBackupManagerServiceMock).getCurrentTransport();
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
}
@Test
@@ -569,28 +685,40 @@ public class TrampolineTest {
}
@Test
- public void listAllTransports_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
+ public void listAllTransportsForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
+ }
+
+ @Test
+ public void listAllTransports_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
- verify(mBackupManagerServiceMock).listAllTransports();
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
}
@Test
- public void listAllTransportComponents_calledBeforeInitialize_ignored() throws RemoteException {
- assertNull(mTrampoline.listAllTransportComponents());
+ public void listAllTransportComponentsForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertNull(mTrampoline.listAllTransportComponentsForUser(mUserId));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void listAllTransportComponents_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
+ public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
TRANSPORT_COMPONENTS);
-
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponents());
- verify(mBackupManagerServiceMock).listAllTransportComponents();
+
+ assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
+ verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId);
}
@Test
@@ -609,21 +737,43 @@ public class TrampolineTest {
}
@Test
- public void describeTransport_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
- "Transport Destination", null, "Data Management");
+ public void updateTransportAttributesForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ mTrampoline.updateTransportAttributesForUser(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void describeTransport_forwarded() throws RemoteException {
+ public void updateTransportAttributesForUser_forwarded() throws RemoteException {
when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
-
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
- "Transport Destination", null, "Data Management");
- verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
- TRANSPORT_NAME, null, "Transport Destination", null, "Data Management");
+
+ mTrampoline.updateTransportAttributesForUser(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
+
+ verify(mBackupManagerServiceMock)
+ .updateTransportAttributes(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
}
@Test
@@ -633,16 +783,31 @@ public class TrampolineTest {
}
@Test
+ public void selectBackupTransportForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
public void selectBackupTransport_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.selectBackupTransport(TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored() throws Exception {
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored()
+ throws Exception {
LinkedBlockingQueue<Integer> q = new LinkedBlockingQueue();
- mTrampoline.selectBackupTransportAsync(
+
+ mTrampoline.selectBackupTransportAsyncForUser(
+ mUserId,
TRANSPORT_COMPONENT_NAME,
new ISelectBackupTransportCallback() {
@Override
@@ -660,6 +825,7 @@ public class TrampolineTest {
return null;
}
});
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
Integer errorCode = q.poll(5, TimeUnit.SECONDS);
assertNotNull(errorCode);
@@ -667,17 +833,19 @@ public class TrampolineTest {
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored_nullListener()
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_nullListener()
throws Exception {
- mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+ mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
// No crash.
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored_listenerThrowException()
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_listenerThrows()
throws Exception {
- mTrampoline.selectBackupTransportAsync(
+ mTrampoline.selectBackupTransportAsyncForUser(
+ mUserId,
TRANSPORT_COMPONENT_NAME,
new ISelectBackupTransportCallback() {
@Override
@@ -695,16 +863,19 @@ public class TrampolineTest {
return null;
}
});
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
// No crash.
}
@Test
- public void selectBackupTransportAsync_forwarded() throws RemoteException {
+ public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
- verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
- null);
+
+ mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
+ verify(mBackupManagerServiceMock)
+ .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null);
}
@Test
@@ -714,14 +885,28 @@ public class TrampolineTest {
}
@Test
- public void getConfigurationIntent_forwarded() throws RemoteException {
+ public void getConfigurationIntentForUser_forwarded() throws RemoteException {
Intent configurationIntentStub = new Intent();
- when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
configurationIntentStub);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(
+ configurationIntentStub,
+ mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
+ public void getConfigurationIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ Intent configurationIntentStub = new Intent();
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+ configurationIntentStub);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -731,13 +916,26 @@ public class TrampolineTest {
}
@Test
+ public void getDestinationStringForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
+ DESTINATION_STRING);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ assertEquals(
+ DESTINATION_STRING,
+ mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
public void getDestinationString_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
DESTINATION_STRING);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
}
@Test
@@ -747,14 +945,28 @@ public class TrampolineTest {
}
@Test
- public void getDataManagementIntent_forwarded() throws RemoteException {
+ public void getDataManagementIntentForUser_forwarded() throws RemoteException {
Intent dataManagementIntent = new Intent();
- when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
dataManagementIntent);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ assertEquals(
+ dataManagementIntent,
+ mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
+ }
+ @Test
+ public void getDataManagementIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ Intent dataManagementIntent = new Intent();
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+ dataManagementIntent);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -764,26 +976,42 @@ public class TrampolineTest {
}
@Test
- public void getDataManagementLabel_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+ public void getDataManagementLabelForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
DATA_MANAGEMENT_LABEL);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ assertEquals(
+ DATA_MANAGEMENT_LABEL,
+ mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
+ }
+ @Test
+ public void getDataManagementLabel_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
+ DATA_MANAGEMENT_LABEL);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
}
@Test
public void beginRestoreSession_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+ mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void beginRestoreSession_forwarded() throws RemoteException {
+ public void beginRestoreSessionForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+
+ mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock)
+ .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
}
@Test
@@ -794,39 +1022,46 @@ public class TrampolineTest {
@Test
public void opComplete_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.opComplete(1, 2);
- verify(mBackupManagerServiceMock).opComplete(1, 2);
+
+ verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
}
@Test
- public void getAvailableRestoreToken_calledBeforeInitialize_ignored() throws RemoteException {
- assertEquals(0, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
+ public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertEquals(0, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void getAvailableRestoreToken_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
-
+ public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
+ .thenReturn(123L);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(123, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
- verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
+
+ assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
+ verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
}
@Test
- public void isAppEligibleForBackup_calledBeforeInitialize_ignored() throws RemoteException {
- assertFalse(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
+ public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertFalse(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void isAppEligibleForBackup_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
-
+ public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
+ .thenReturn(true);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertTrue(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
- verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
+
+ assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
+ verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
}
@Test
@@ -989,6 +1224,11 @@ public class TrampolineTest {
return sBackupManagerServiceMock;
}
+ @Override
+ protected void postToHandler(Runnable runnable) {
+ runnable.run();
+ }
+
int getCreateServiceCallsCount() {
return mCreateServiceCallsCount;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index e9bfa8f4e0c8..abf90402250c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -149,12 +149,11 @@ public class DisplayManagerServiceTest {
verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
List<DisplayViewport> viewports = viewportCaptor.getValue();
- // Expect to receive 3 viewports: internal, external, and virtual
- assertEquals(3, viewports.size());
+ // Expect to receive 2 viewports: internal, and virtual
+ assertEquals(2, viewports.size());
DisplayViewport virtualViewport = null;
DisplayViewport internalViewport = null;
- DisplayViewport externalViewport = null;
for (int i = 0; i < viewports.size(); i++) {
DisplayViewport v = viewports.get(i);
switch (v.type) {
@@ -163,7 +162,7 @@ public class DisplayManagerServiceTest {
break;
}
case DisplayViewport.VIEWPORT_EXTERNAL: {
- externalViewport = v;
+ fail("EXTERNAL viewport should not exist.");
break;
}
case DisplayViewport.VIEWPORT_VIRTUAL: {
@@ -172,14 +171,12 @@ public class DisplayManagerServiceTest {
}
}
}
- // INTERNAL and EXTERNAL viewports get created upon access
+ // INTERNAL viewport gets created upon access.
assertNotNull(internalViewport);
- assertNotNull(externalViewport);
assertNotNull(virtualViewport);
- // INTERNAL and EXTERNAL
+ // INTERNAL
assertTrue(internalViewport.valid);
- assertTrue(externalViewport.valid);
// VIRTUAL
assertEquals(height, virtualViewport.deviceHeight);
@@ -216,39 +213,16 @@ public class DisplayManagerServiceTest {
verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
List<DisplayViewport> viewports = viewportCaptor.getValue();
- // Expect to receive 2 viewports: 1 internal, 1 external
- assertEquals(2, viewports.size());
+ // Expect to receive actual viewports: 1 internal
+ assertEquals(1, viewports.size());
- DisplayViewport internalViewport = null;
- DisplayViewport externalViewport = null;
- for (int i = 0; i < viewports.size(); i++) {
- DisplayViewport v = viewports.get(i);
- switch (v.type) {
- case DisplayViewport.VIEWPORT_INTERNAL: {
- internalViewport = v;
- break;
- }
- case DisplayViewport.VIEWPORT_EXTERNAL: {
- externalViewport = v;
- break;
- }
- default: {
- fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
- break;
- }
- }
- }
- // INTERNAL and EXTERNAL viewports get created upon access
+ DisplayViewport internalViewport = viewports.get(0);
+
+ // INTERNAL is the only one actual display.
assertNotNull(internalViewport);
- assertNotNull(externalViewport);
+ assertEquals(DisplayViewport.VIEWPORT_INTERNAL, internalViewport.type);
assertTrue(internalViewport.valid);
assertEquals(displayId, internalViewport.displayId);
-
- // To simplify comparison, override the type for external Viewport
- // TODO (b/116850516) remove this
- externalViewport.type = internalViewport.type;
- assertEquals(internalViewport, externalViewport);
- externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 7755e94369af..5df4509af885 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -17,6 +17,7 @@
package com.android.server.pm.dex;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static org.junit.Assert.assertEquals;
@@ -31,24 +32,28 @@ import android.os.Build;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import dalvik.system.VMRuntime;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import dalvik.system.VMRuntime;
-
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageDexUsageTests {
+ private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+
private PackageDexUsage mPackageDexUsage;
private TestData mFooBaseUser0;
@@ -71,25 +76,23 @@ public class PackageDexUsageTests {
String fooCodeDir = "/data/app/com.google.foo/";
String fooDataDir = "/data/user/0/com.google.foo/";
- String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-
mFooBaseUser0 = new TestData(fooPackageName,
- fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
+ fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName);
mFooSplit1User0 = new TestData(fooPackageName,
- fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
+ fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName);
mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
- fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
+ fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com");
mFooSecondary1User0 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
+ fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName);
mFooSecondary1User1 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
+ fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName);
mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
- fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
+ fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com");
mInvalidIsa = new TestData(fooPackageName,
fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
@@ -100,11 +103,11 @@ public class PackageDexUsageTests {
String barDataDir1 = "/data/user/1/com.google.bar/";
mBarBaseUser0 = new TestData(barPackageName,
- barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
+ barCodeDir + "base.apk", 0, ISA, false, true, barPackageName);
mBarSecondary1User0 = new TestData(barPackageName,
- barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
+ barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName);
mBarSecondary2User1 = new TestData(barPackageName,
- barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
+ barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName);
}
@Test
@@ -183,6 +186,25 @@ public class PackageDexUsageTests {
}
@Test
+ public void testRecordTooManySecondaries() {
+ int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1;
+ List<TestData> expectedSecondaries = new ArrayList<>();
+ for (int i = 1; i <= tooManyFiles; i++) {
+ String fooPackageName = "com.google.foo";
+ TestData testData = new TestData(fooPackageName,
+ "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false,
+ fooPackageName);
+ if (i < tooManyFiles) {
+ assertTrue("Adding " + testData.mDexFile, record(testData));
+ expectedSecondaries.add(testData);
+ } else {
+ assertFalse("Adding " + testData.mDexFile, record(testData));
+ }
+ assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries);
+ }
+ }
+
+ @Test
public void testMultiplePackages() {
assertTrue(record(mFooBaseUser0));
assertTrue(record(mFooSecondary1User0));
@@ -540,7 +562,14 @@ public class PackageDexUsageTests {
private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
TestData primary, TestData... secondaries) {
- String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
+ assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries));
+ }
+
+ private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+ TestData primary, List<TestData> secondaries) {
+ String packageName = primary == null
+ ? secondaries.get(0).mPackageName
+ : primary.mPackageName;
boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
@@ -554,7 +583,7 @@ public class PackageDexUsageTests {
}
Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
- assertEquals(secondaries.length, dexUseInfoMap.size());
+ assertEquals(secondaries.size(), dexUseInfoMap.size());
// Check dex use info
for (TestData testData : secondaries) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 527a1eef7b0f..371e8f4db9de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -208,6 +208,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private static class TestableNotificationManagerService extends NotificationManagerService {
int countSystemChecks = 0;
boolean isSystemUid = true;
+ int countLogSmartSuggestionsVisible = 0;
public TestableNotificationManagerService(Context context) {
super(context);
@@ -231,19 +232,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Override
- protected void reportSeen(NotificationRecord r) {
+ protected void reportUserInteraction(NotificationRecord r) {
return;
}
@Override
- protected void reportUserInteraction(NotificationRecord r) {
+ protected void handleSavePolicyFile() {
return;
}
@Override
- protected void handleSavePolicyFile() {
- return;
+ void logSmartSuggestionsVisible(NotificationRecord r) {
+ super.logSmartSuggestionsVisible(r);
+ countLogSmartSuggestionsVisible++;
}
+
+
}
private class TestableToastCallback extends ITransientNotification.Stub {
@@ -3507,6 +3511,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testAppOverlay() throws Exception {
+ mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
+ assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+ }
+
+ @Test
public void testIsCallerInstantApp_primaryUser() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
@@ -3777,4 +3787,66 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mAssistants).notifyAssistantActionClicked(
eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
}
+
+ @Test
+ public void testLogSmartSuggestionsVisible_triggerOnExpandAndVisible() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ NotificationVisibility[] notificationVisibility = new NotificationVisibility[] {
+ NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+ };
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+ new NotificationVisibility[0]);
+
+ assertEquals(1, mService.countLogSmartSuggestionsVisible);
+ }
+
+ @Test
+ public void testLogSmartSuggestionsVisible_noTriggerOnExpand() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+
+ assertEquals(0, mService.countLogSmartSuggestionsVisible);
+ }
+
+ @Test
+ public void testLogSmartSuggestionsVisible_noTriggerOnVisible() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ NotificationVisibility[] notificationVisibility = new NotificationVisibility[]{
+ NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+ };
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+ new NotificationVisibility[0]);
+
+ assertEquals(0, mService.countLogSmartSuggestionsVisible);
+ }
+
+ public void testReportSeen_delegated() {
+ Notification.Builder nb =
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, "opPkg", 0, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.reportSeen(r);
+ verify(mAppUsageStats, never()).reportEvent(anyString(), anyInt(), anyInt());
+
+ }
+
+ @Test
+ public void testReportSeen_notDelegated() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+ mService.reportSeen(r);
+ verify(mAppUsageStats, times(1)).reportEvent(anyString(), anyInt(), anyInt());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b0279357d372..0b7348194b26 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1584,39 +1584,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testUpdateGroup_fromSystem_appOverlay() {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
- // from system, allowed
- NotificationChannelGroup update = ncg.clone();
- update.setAllowAppOverlay(false);
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false);
- NotificationChannelGroup updated =
- mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
- assertFalse(updated.canOverlayApps());
- assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY,
- updated.getUserLockedFields());
- }
-
- @Test
- public void testUpdateGroup_fromApp_appOverlay() {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
- // from app, not allowed
- NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1");
- update.setAllowAppOverlay(false);
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
- NotificationChannelGroup updated =
- mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
- assertTrue(updated.canOverlayApps());
- assertEquals(0, updated.getUserLockedFields());
- }
-
- @Test
public void testCannotCreateChannel_badGroup() {
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -2192,4 +2159,32 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
}
+
+ @Test
+ public void testAllowAppOverlay_defaults() throws Exception {
+ assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testAllowAppOverlay_xml() throws Exception {
+ mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
+ assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index b955e56c80a8..49f134fdeac0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -76,7 +76,7 @@ public class SnoozeHelperTest extends UiServiceTestCase {
verify(mAm, times(1)).setExactAndAllowWhileIdle(
anyInt(), captor.capture(), any(PendingIntent.class));
long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
- assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 25);
+ assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e9889948c341..bf4b52eb72aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -52,7 +52,6 @@ import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.view.Surface;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -81,7 +80,6 @@ import java.util.concurrent.TimeUnit;
*/
@SmallTest
@Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
public class DisplayRotationTests {
private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index f3a125bf79e4..86353643c128 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -40,7 +40,6 @@ import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.server.LocalServices;
@@ -65,7 +64,6 @@ import java.util.function.Predicate;
*/
@MediumTest
@Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
public class LaunchParamsPersisterTests extends ActivityTestsBase {
private static final int TEST_USER_ID = 3;
private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 9b18388b5305..58302d6b8b75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -23,8 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -36,7 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
-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;
@@ -47,18 +47,27 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
import android.graphics.Rect;
-import android.os.Build;
import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
import androidx.test.filters.MediumTest;
+
+import com.android.internal.app.ResolverActivity;
+
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
+import java.util.List;
/**
* Tests for the {@link ActivityStackSupervisor} class.
@@ -385,31 +394,10 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
/**
- * Tests home activities that targeted sdk before Q cannot start on secondary display.
- */
- @Test
- public void testStartHomeTargetSdkBeforeQ() throws Exception {
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- final ActivityInfo info = new ActivityInfo();
- info.launchMode = LAUNCH_MULTIPLE;
- info.applicationInfo = new ApplicationInfo();
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
-
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
- assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
- }
-
- /**
* Tests that home activities can be started on the displays that supports system decorations.
*/
- @Test
- public void testStartHomeOnAllDisplays() {
+ // TODO (b/118206886): Will add it back once launcher's patch is merged into master.
+ private void testStartHomeOnAllDisplays() {
// Create secondary displays.
final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
@@ -477,4 +465,142 @@ public class RootActivityContainerTests extends ActivityTestsBase {
assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
true /* allowInstrumenting*/));
}
+
+ /**
+ * Tests that secondary home should be selected if default home not set.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
+ final Intent defaultHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = ResolverActivity.class.getName();
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(defaultHomeIntent));
+
+ final String secondaryHomeComponent = mService.mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+ final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+ final ActivityInfo aInfoSecondary = new ActivityInfo();
+ aInfoSecondary.name = comp.getClassName();
+ doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(secondaryHomeIntent));
+
+ // Should fallback to secondary home if default home not set.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that secondary home should be selected if default home not support secondary displays
+ * or there is no matched activity in the same package as selected default home.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
+ final Intent defaultHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(defaultHomeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ final String secondaryHomeComponent = mService.mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+ final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+ final ActivityInfo aInfoSecondary = new ActivityInfo();
+ aInfoSecondary.name = comp.getClassName();
+ doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(secondaryHomeIntent));
+
+ // Should fallback to secondary home if selected default home not support secondary displays
+ // or there is no matched activity in the same package as selected default home.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that default home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ infoFake2.activityInfo = aInfoDefault;
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ // Use default home activity if it support secondary displays.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(aInfoDefault.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(aInfoDefault.name, resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that the first one that matches should be selected if there are multiple activities.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ infoFake2.activityInfo = new ActivityInfo();
+ infoFake2.activityInfo.name = "fakeActivity2";
+ infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ // Use the first one of matched activities in the same package as selected default home.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 0bd681bd69a2..7186e22cefef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -47,7 +47,6 @@ import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.Gravity;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -64,7 +63,6 @@ import java.util.Locale;
* atest WmTests:TaskLaunchParamsModifierTests
*/
@SmallTest
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
@Presubmit
public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 3991e06d6f96..c343fe7d0675 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertFalse;
@@ -66,6 +67,10 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
synchronized (mWm.mGlobalLock) {
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
+
+ spyOn(mDisplayContent);
+ InputMonitor inputMonitor = mock(InputMonitor.class);
+ when(mDisplayContent.getInputMonitor()).thenReturn(inputMonitor);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 522ab9ffb291..04e433e98678 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -164,6 +164,7 @@ public class WindowManagerServiceRule implements TestRule {
}
private void tearDown() {
+ cancelAllPendingAnimations();
waitUntilWindowManagerHandlersIdle();
destroyAllSurfaceTransactions();
destroyAllSurfaceControls();
@@ -178,6 +179,15 @@ public class WindowManagerServiceRule implements TestRule {
return mService;
}
+ private void cancelAllPendingAnimations() {
+ for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+ final SurfaceControl sc = reference.get();
+ if (sc != null) {
+ mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
+ }
+ }
+ }
+
void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
if (wm == null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index ec475bf26bb3..eddf8f9b7254 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -98,14 +98,14 @@ final class UsageStatsXmlV1 {
stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_VISIBLE_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+ Log.i(TAG, "Failed to parse mLastTimeVisible");
}
try {
stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+ Log.i(TAG, "Failed to parse mLastTimeForegroundServiceUsed");
}
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
@@ -113,14 +113,14 @@ final class UsageStatsXmlV1 {
try {
stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+ Log.i(TAG, "Failed to parse mTotalTimeVisible");
}
try {
stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
TOTAL_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+ Log.i(TAG, "Failed to parse mTotalTimeForegroundServiceUsed");
}
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 60cb08f00f89..294b7509698b 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,12 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import static com.android.internal.usb.DumpUtils.writeAccessory;
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
@@ -36,6 +42,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
import android.debug.IAdbTransport;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
@@ -294,9 +301,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
BroadcastReceiver portReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+ ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
- mHandler.updateHostState(port, status);
+ mHandler.updateHostState(
+ port.getUsbPort(context.getSystemService(UsbManager.class)), status);
}
};
@@ -821,23 +829,20 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
boolean prevHostConnected = mHostConnected;
UsbPort port = (UsbPort) args.arg1;
UsbPortStatus status = (UsbPortStatus) args.arg2;
- mHostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
- mSourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
- mSinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
- mAudioAccessoryConnected =
- (status.getCurrentMode() == UsbPort.MODE_AUDIO_ACCESSORY);
- mAudioAccessorySupported = port.isModeSupported(UsbPort.MODE_AUDIO_ACCESSORY);
+ mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+ mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+ mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+ mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+ mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
// Ideally we want to see if PR_SWAP and DR_SWAP is supported.
// But, this should be suffice, since, all four combinations are only supported
// when PR_SWAP and DR_SWAP are supported.
mSupportsAllCombinations = status.isRoleCombinationSupported(
- UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
- UsbPort.DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE,
- UsbPort.DATA_ROLE_DEVICE)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
- UsbPort.DATA_ROLE_HOST);
+ POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+ DATA_ROLE_DEVICE)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST);
args.recycle();
updateUsbNotification(false);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 96618f569928..6f210e37d6d1 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,12 +16,22 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import static com.android.internal.usb.DumpUtils.writePort;
import static com.android.internal.usb.DumpUtils.writePortStatus;
+import android.Manifest;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -78,13 +88,13 @@ public class UsbPortManager {
// All non-trivial role combinations.
private static final int COMBO_SOURCE_HOST =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
- private static final int COMBO_SOURCE_DEVICE =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+ UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
+ private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit(
+ POWER_ROLE_SOURCE, DATA_ROLE_DEVICE);
private static final int COMBO_SINK_HOST =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
- private static final int COMBO_SINK_DEVICE =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+ UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST);
+ private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit(
+ POWER_ROLE_SINK, DATA_ROLE_DEVICE);
// The system context.
private final Context mContext;
@@ -217,12 +227,12 @@ public class UsbPortManager {
final int newMode;
if ((!canChangePowerRole && currentPowerRole != newPowerRole)
|| (!canChangeDataRole && currentDataRole != newDataRole)) {
- if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
- && newDataRole == UsbPort.DATA_ROLE_HOST) {
- newMode = UsbPort.MODE_DFP;
- } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
- && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
- newMode = UsbPort.MODE_UFP;
+ if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE
+ && newDataRole == DATA_ROLE_HOST) {
+ newMode = MODE_DFP;
+ } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK
+ && newDataRole == DATA_ROLE_DEVICE) {
+ newMode = MODE_UFP;
} else {
logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
+ "while attempting to change role: " + portInfo
@@ -607,7 +617,7 @@ public class UsbPortManager {
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
- if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) {
+ if ((supportedModes & MODE_DUAL) != MODE_DUAL) {
canChangeMode = false;
if (currentMode != 0 && currentMode != supportedModes) {
logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
@@ -633,16 +643,16 @@ public class UsbPortManager {
// Can only change power role.
// Assume data role must remain at its current value.
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+ POWER_ROLE_SOURCE, currentDataRole);
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- UsbPort.POWER_ROLE_SINK, currentDataRole);
+ POWER_ROLE_SINK, currentDataRole);
} else if (canChangeDataRole) {
// Can only change data role.
// Assume power role must remain at its current value.
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- currentPowerRole, UsbPort.DATA_ROLE_HOST);
+ currentPowerRole, DATA_ROLE_HOST);
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+ currentPowerRole, DATA_ROLE_DEVICE);
} else if (canChangeMode) {
// Can only change the mode.
// Assume both standard UFP and DFP configurations will become available
@@ -654,7 +664,8 @@ public class UsbPortManager {
// Update the port data structures.
PortInfo portInfo = mPorts.get(portId);
if (portInfo == null) {
- portInfo = new PortInfo(portId, supportedModes);
+ portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
+ supportedModes);
portInfo.setStatus(currentMode, canChangeMode,
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
@@ -701,12 +712,13 @@ public class UsbPortManager {
intent.addFlags(
Intent.FLAG_RECEIVER_FOREGROUND |
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+ intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort));
intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
// Guard against possible reentrance by posting the broadcast from the handler
// instead of from within the critical section.
- mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+ mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ Manifest.permission.MANAGE_USB));
// Log to statsd
if (!mConnected.containsKey(portInfo.mUsbPort.getId())
@@ -772,8 +784,8 @@ public class UsbPortManager {
// 0 when port is connected. Else reports the last connected duration
public long mLastConnectDurationMillis;
- public PortInfo(String portId, int supportedModes) {
- mUsbPort = new UsbPort(portId, supportedModes);
+ PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
+ mUsbPort = new UsbPort(usbManager, portId, supportedModes);
}
public boolean setStatus(int currentMode, boolean canChangeMode,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f9abedfbf586..911547720a8f 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,14 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
@@ -27,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
@@ -52,7 +61,9 @@ import com.android.server.SystemService;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
* UsbService manages all USB related state, including both host and device support.
@@ -489,12 +500,25 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
- public UsbPort[] getPorts() {
+ public List<ParcelableUsbPort> getPorts() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
try {
- return mPortManager != null ? mPortManager.getPorts() : null;
+ if (mPortManager == null) {
+ return null;
+ } else {
+ final UsbPort[] ports = mPortManager.getPorts();
+
+ final int numPorts = ports.length;
+ ArrayList<ParcelableUsbPort> parcelablePorts = new ArrayList<>();
+ for (int i = 0; i < numPorts; i++) {
+ parcelablePorts.add(ParcelableUsbPort.of(ports[i]));
+ }
+
+ return parcelablePorts;
+ }
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -588,10 +612,10 @@ public class UsbService extends IUsbManager.Stub {
final int powerRole;
switch (args[2]) {
case "source":
- powerRole = UsbPort.POWER_ROLE_SOURCE;
+ powerRole = POWER_ROLE_SOURCE;
break;
case "sink":
- powerRole = UsbPort.POWER_ROLE_SINK;
+ powerRole = POWER_ROLE_SINK;
break;
case "no-power":
powerRole = 0;
@@ -603,10 +627,10 @@ public class UsbService extends IUsbManager.Stub {
final int dataRole;
switch (args[3]) {
case "host":
- dataRole = UsbPort.DATA_ROLE_HOST;
+ dataRole = DATA_ROLE_HOST;
break;
case "device":
- dataRole = UsbPort.DATA_ROLE_DEVICE;
+ dataRole = DATA_ROLE_DEVICE;
break;
case "no-data":
dataRole = 0;
@@ -631,13 +655,13 @@ public class UsbService extends IUsbManager.Stub {
final int supportedModes;
switch (args[2]) {
case "ufp":
- supportedModes = UsbPort.MODE_UFP;
+ supportedModes = MODE_UFP;
break;
case "dfp":
- supportedModes = UsbPort.MODE_DFP;
+ supportedModes = MODE_DFP;
break;
case "dual":
- supportedModes = UsbPort.MODE_DUAL;
+ supportedModes = MODE_DUAL;
break;
case "none":
supportedModes = 0;
@@ -658,10 +682,10 @@ public class UsbService extends IUsbManager.Stub {
final boolean canChangeMode = args[2].endsWith("?");
switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
case "ufp":
- mode = UsbPort.MODE_UFP;
+ mode = MODE_UFP;
break;
case "dfp":
- mode = UsbPort.MODE_DFP;
+ mode = MODE_DFP;
break;
default:
pw.println("Invalid mode: " + args[2]);
@@ -671,10 +695,10 @@ public class UsbService extends IUsbManager.Stub {
final boolean canChangePowerRole = args[3].endsWith("?");
switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
case "source":
- powerRole = UsbPort.POWER_ROLE_SOURCE;
+ powerRole = POWER_ROLE_SOURCE;
break;
case "sink":
- powerRole = UsbPort.POWER_ROLE_SINK;
+ powerRole = POWER_ROLE_SINK;
break;
default:
pw.println("Invalid power role: " + args[3]);
@@ -684,10 +708,10 @@ public class UsbService extends IUsbManager.Stub {
final boolean canChangeDataRole = args[4].endsWith("?");
switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
case "host":
- dataRole = UsbPort.DATA_ROLE_HOST;
+ dataRole = DATA_ROLE_HOST;
break;
case "device":
- dataRole = UsbPort.DATA_ROLE_DEVICE;
+ dataRole = DATA_ROLE_DEVICE;
break;
default:
pw.println("Invalid data role: " + args[4]);
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 94879d0bbcc4..a78f7d53d135 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -79,6 +79,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
case Instruction::Op::kNew:
out << "kNew";
return out;
+ case Instruction::Op::kCheckCast:
+ out << "kCheckCast";
+ return out;
}
}
@@ -329,6 +332,8 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
return EncodeBranch(art::Instruction::IF_NEZ, instruction);
case Instruction::Op::kNew:
return EncodeNew(instruction);
+ case Instruction::Op::kCheckCast:
+ return EncodeCast(instruction);
}
}
@@ -422,6 +427,18 @@ void MethodBuilder::EncodeNew(const Instruction& instruction) {
Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
}
+void MethodBuilder::EncodeCast(const Instruction& instruction) {
+ DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
+ DCHECK(instruction.dest().has_value());
+ DCHECK(instruction.dest()->is_variable());
+ DCHECK_EQ(1, instruction.args().size());
+
+ const Value& type = instruction.args()[0];
+ DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+ DCHECK(type.is_type());
+ Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
+}
+
size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 45596acfdc44..06059c8c6e56 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -142,17 +142,18 @@ class Instruction {
// The operation performed by this instruction. These are virtual instructions that do not
// correspond exactly to DEX instructions.
enum class Op {
- kReturn,
- kReturnObject,
- kMove,
- kInvokeVirtual,
- kInvokeDirect,
- kInvokeStatic,
- kInvokeInterface,
kBindLabel,
kBranchEqz,
kBranchNEqz,
- kNew
+ kCheckCast,
+ kInvokeDirect,
+ kInvokeInterface,
+ kInvokeStatic,
+ kInvokeVirtual,
+ kMove,
+ kNew,
+ kReturn,
+ kReturnObject,
};
////////////////////////
@@ -168,6 +169,13 @@ class Instruction {
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
}
+
+ // A cast instruction. Basically, `(type)val`
+ static inline Instruction Cast(Value val, Value type) {
+ DCHECK(type.is_type());
+ return OpWithArgs(Op::kCheckCast, val, type);
+ }
+
// For method calls.
template <typename... T>
static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
@@ -302,6 +310,7 @@ class MethodBuilder {
void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
void EncodeNew(const Instruction& instruction);
+ void EncodeCast(const Instruction& instruction);
// Low-level instruction format encoding. See
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 1508ad9ee56b..42d4161ee81e 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -20,6 +20,7 @@ import com.google.common.io.ByteStreams;
import dalvik.system.InMemoryDexClassLoader;
import dalvik.system.PathClassLoader;
import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import org.junit.Assert;
@@ -151,4 +152,23 @@ public class DexBuilderTest {
Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
Assert.assertEquals("bc", method.invoke(null, "abc", 1));
}
+
+ @Test
+ public void castObjectToString() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("castObjectToString", Object.class);
+ Assert.assertEquals("abc", method.invoke(null, "abc"));
+ boolean castFailed = false;
+ try {
+ method.invoke(null, 5);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof ClassCastException) {
+ castFailed = true;
+ } else {
+ throw e;
+ }
+ }
+ Assert.assertTrue(castFailed);
+ }
}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 2781aa55d1df..f62ec5dde85e 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -269,6 +269,19 @@ void GenerateSimpleTestCases(const string& outdir) {
method.Encode();
}(invokeVirtualReturnObject);
+ // Make sure we can cast objects
+ // String castObjectToString(Object o) { return (String)o; }
+ MethodBuilder castObjectToString{cbuilder.CreateMethod(
+ "castObjectToString",
+ Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})};
+ [&](MethodBuilder& method) {
+ const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor());
+ method.AddInstruction(
+ Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index)));
+ method.BuildReturn(Value::Parameter(0), /*is_object=*/true);
+ method.Encode();
+ }(castObjectToString);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
new file mode 100644
index 000000000000..e2fa7e4032c1
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable PhoneAccountSuggestion; \ No newline at end of file
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
index 4e6a178c8170..b401bcf0f876 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -24,6 +24,7 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
public final class PhoneAccountSuggestion implements Parcelable {
@@ -132,4 +133,19 @@ public final class PhoneAccountSuggestion implements Parcelable {
dest.writeInt(mReason);
dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneAccountSuggestion that = (PhoneAccountSuggestion) o;
+ return mReason == that.mReason
+ && mShouldAutoSelect == that.mShouldAutoSelect
+ && Objects.equals(mHandle, that.mHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mHandle, mReason, mShouldAutoSelect);
+ }
}
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
new file mode 100644
index 000000000000..ba3822cb9951
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+import com.android.internal.telecom.IPhoneAccountSuggestionService;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for service that allows system apps to suggest phone accounts for outgoing calls.
+ *
+ * Phone account suggestions allow OEMs to intelligently select phone accounts based on knowledge
+ * about the user's past behavior, carrier billing patterns, or other factors unknown to the AOSP
+ * Telecom system.
+ * OEMs who wish to provide a phone account suggestion service on their device should implement this
+ * service in an app that resides in the /system/priv-app/ directory on their device. For security
+ * reasons, the service's entry {@code AndroidManifest.xml} file must declare the
+ * {@link android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE} permission:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourServiceName"
+ * android:permission="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.PhoneAccountSuggestionService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * Only one system app on each device may implement this service. If multiple system apps implement
+ * this service, none of them will be queried for suggestions.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class PhoneAccountSuggestionService extends Service {
+ /**
+ * The {@link Intent} that must be declared in the {@code intent-filter} element of the
+ * service's manifest entry.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+
+ private IPhoneAccountSuggestionService mInterface = new IPhoneAccountSuggestionService.Stub() {
+ @Override
+ public void onAccountSuggestionRequest(IPhoneAccountSuggestionCallback callback,
+ String number) {
+ mCallbackMap.put(number, callback);
+ PhoneAccountSuggestionService.this.onAccountSuggestionRequest(number);
+ }
+ };
+
+ private final Map<String, IPhoneAccountSuggestionCallback> mCallbackMap =
+ new HashMap<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mInterface.asBinder();
+ }
+
+ /**
+ * The system calls this method during the outgoing call flow if it needs account suggestions.
+ *
+ * The implementer of this service must override this method to implement its account suggestion
+ * logic. After preparing the suggestions, the implementation of the service must call
+ * {@link #suggestPhoneAccounts(String, List)} to deliver the suggestions back to the system.
+ *
+ * Note that the system will suspend the outgoing call process after it calls this method until
+ * this service calls {@link #suggestPhoneAccounts}.
+ *
+ * @param number The phone number to provide suggestions for.
+ */
+ public void onAccountSuggestionRequest(@NonNull String number) {}
+
+ /**
+ * The implementation of this service calls this method to deliver suggestions to the system.
+ *
+ * The implementation of this service must call this method after receiving a call to
+ * {@link #onAccountSuggestionRequest(String)}. If no suggestions are available, pass an empty
+ * list as the {@code suggestions} argument.
+ *
+ * @param number The phone number to provide suggestions for.
+ * @param suggestions The list of suggestions.
+ */
+ public final void suggestPhoneAccounts(@NonNull String number,
+ @NonNull List<PhoneAccountSuggestion> suggestions) {
+ IPhoneAccountSuggestionCallback callback = mCallbackMap.remove(number);
+ if (callback == null) {
+ Log.w(this, "No suggestions requested for the number %s", Log.pii(number));
+ return;
+ }
+ try {
+ callback.suggestPhoneAccounts(number, suggestions);
+ } catch (RemoteException e) {
+ Log.w(this, "Remote exception calling suggestPhoneAccounts");
+ }
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
new file mode 100644
index 000000000000..cb142417451c
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.telecom.PhoneAccountSuggestion;
+/**
+ * Internal remote callback interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionCallback{
+ void suggestPhoneAccounts(in String number, in List<PhoneAccountSuggestion> suggestions);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
new file mode 100644
index 000000000000..0ffab93d9f1b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+
+/**
+ * Internal remote interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionService {
+ void onAccountSuggestionRequest(in IPhoneAccountSuggestionCallback callback,
+ in String number);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 50204e70f63d..954a7098f6be 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -292,6 +292,8 @@ interface ITelecomService {
void setTestDefaultCallRedirectionApp(String packageName);
+ void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
+
void setTestDefaultCallScreeningApp(String packageName);
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fd14916ecac9..f87472d9ec38 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2400,6 +2400,34 @@ public class CarrierConfigManager {
public static final String KEY_5G_ICON_CONFIGURATION_STRING =
"5g_icon_configuration_string";
+ /**
+ * Controls RSRP threshold at which AlternativeNetworkService will decide whether
+ * the opportunistic network is good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
+ "opportunistic_network_entry_threshold_rsrp_int";
+
+ /**
+ * Controls RSSNR threshold at which AlternativeNetworkService will decide whether
+ * the opportunistic network is good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
+ "opportunistic_network_entry_threshold_rssnr_int";
+
+ /**
+ * Controls RSRP threshold below which AlternativeNetworkService will decide whether
+ * the opportunistic network available is not good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
+ "opportunistic_network_exit_threshold_rsrp_int";
+
+ /**
+ * Controls RSSNR threshold below which AlternativeNetworkService will decide whether
+ * the opportunistic network available is not good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
+ "opportunistic_network_exit_threshold_rssnr_int";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2761,6 +2789,14 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+ /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
+ /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+ /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
+ /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 04c28e5211c8..c8a899b339c6 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -169,6 +170,7 @@ public final class CellIdentityGsm extends CellIdentity {
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 04b6a6ca7fea..8e1877d8e35f 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -197,6 +198,7 @@ public final class CellIdentityLte extends CellIdentity {
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8b1c1b9f024c..f77c468d2f5e 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -116,6 +117,7 @@ public final class CellIdentityTdscdma extends CellIdentity {
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 3416ffe0b8f4..31f9e6d5bb90 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -173,6 +174,7 @@ public final class CellIdentityWcdma extends CellIdentity {
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c6f7d0e458db..c53b37d8ae6a 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -15,151 +15,383 @@
*/
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.PersistableBundle;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
- * Returned as the reason for a connection failure as defined
- * by RIL_DataCallFailCause in ril.h and some local errors.
+ * Returned as the reason for a data connection failure as defined by modem and some local errors.
* @hide
*/
-public enum DataFailCause {
- NONE(0),
+public final class DataFailCause {
+ /** There is no failure */
+ public static final int NONE = 0;
// This series of errors as specified by the standards
// specified in ril.h
- OPERATOR_BARRED(0x08), /* no retry */
- NAS_SIGNALLING(0x0E),
- LLC_SNDCP(0x19),
- INSUFFICIENT_RESOURCES(0x1A),
- MISSING_UNKNOWN_APN(0x1B), /* no retry */
- UNKNOWN_PDP_ADDRESS_TYPE(0x1C), /* no retry */
- USER_AUTHENTICATION(0x1D), /* no retry */
- ACTIVATION_REJECT_GGSN(0x1E), /* no retry */
- ACTIVATION_REJECT_UNSPECIFIED(0x1F),
- SERVICE_OPTION_NOT_SUPPORTED(0x20), /* no retry */
- SERVICE_OPTION_NOT_SUBSCRIBED(0x21), /* no retry */
- SERVICE_OPTION_OUT_OF_ORDER(0x22),
- NSAPI_IN_USE(0x23), /* no retry */
- REGULAR_DEACTIVATION(0x24), /* possibly restart radio, based on config */
- QOS_NOT_ACCEPTED(0x25),
- NETWORK_FAILURE(0x26),
- UMTS_REACTIVATION_REQ(0x27),
- FEATURE_NOT_SUPP(0x28),
- TFT_SEMANTIC_ERROR(0x29),
- TFT_SYTAX_ERROR(0x2A),
- UNKNOWN_PDP_CONTEXT(0x2B),
- FILTER_SEMANTIC_ERROR(0x2C),
- FILTER_SYTAX_ERROR(0x2D),
- PDP_WITHOUT_ACTIVE_TFT(0x2E),
- ONLY_IPV4_ALLOWED(0x32), /* no retry */
- ONLY_IPV6_ALLOWED(0x33), /* no retry */
- ONLY_SINGLE_BEARER_ALLOWED(0x34),
- ESM_INFO_NOT_RECEIVED(0x35),
- PDN_CONN_DOES_NOT_EXIST(0x36),
- MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
- MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
- UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
- INVALID_TRANSACTION_ID(0x51),
- MESSAGE_INCORRECT_SEMANTIC(0x5F),
- INVALID_MANDATORY_INFO(0x60),
- MESSAGE_TYPE_UNSUPPORTED(0x61),
- MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
- UNKNOWN_INFO_ELEMENT(0x63),
- CONDITIONAL_IE_ERROR(0x64),
- MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
- PROTOCOL_ERRORS(0x6F), /* no retry */
- APN_TYPE_CONFLICT(0x70),
- INVALID_PCSCF_ADDR(0x71),
- INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
- EMM_ACCESS_BARRED(0x73),
- EMERGENCY_IFACE_ONLY(0x74),
- IFACE_MISMATCH(0x75),
- COMPANION_IFACE_IN_USE(0x76),
- IP_ADDRESS_MISMATCH(0x77),
- IFACE_AND_POL_FAMILY_MISMATCH(0x78),
- EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
- AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+ /** Operator determined barring. */
+ public static final int OPERATOR_BARRED = 0x08;
+ /** NAS signalling. */
+ public static final int NAS_SIGNALLING = 0x0E;
+ /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */
+ public static final int LLC_SNDCP = 0x19;
+ /** Insufficient resources. */
+ public static final int INSUFFICIENT_RESOURCES = 0x1A;
+ /** Missing or unknown APN. */
+ public static final int MISSING_UNKNOWN_APN = 0x1B; /* no retry */
+ /** Unknown Packet Data Protocol (PDP) address type. */
+ public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; /* no retry */
+ /** User authentication. */
+ public static final int USER_AUTHENTICATION = 0x1D; /* no retry */
+ /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */
+ public static final int ACTIVATION_REJECT_GGSN = 0x1E; /* no retry */
+ /** Activation rejected, unspecified. */
+ public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
+ /** Service option not supported. */
+ public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20; /* no retry */
+ /** Requested service option not subscribed. */
+ public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; /* no retry */
+ /** Service option temporarily out of order. */
+ public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22;
+ /** The Network Service Access Point Identifier (NSAPI) is in use. */
+ public static final int NSAPI_IN_USE = 0x23; /* no retry */
+ /* possibly restart radio, based on config */
+ /** Regular deactivation. */
+ public static final int REGULAR_DEACTIVATION = 0x24;
+ /** Quality of service (QoS) is not accepted. */
+ public static final int QOS_NOT_ACCEPTED = 0x25;
+ /** Network Failure. */
+ public static final int NETWORK_FAILURE = 0x26;
+ /** Universal Mobile Telecommunications System (UMTS) reactivation request. */
+ public static final int UMTS_REACTIVATION_REQ = 0x27;
+ /** Feature not supported. */
+ public static final int FEATURE_NOT_SUPP = 0x28;
+ /** Semantic error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SEMANTIC_ERROR = 0x29;
+ /** Syntactical error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SYTAX_ERROR = 0x2A;
+ /** Unknown Packet Data Protocol (PDP) context. */
+ public static final int UNKNOWN_PDP_CONTEXT = 0x2B;
+ /** Semantic errors in packet filter. */
+ public static final int FILTER_SEMANTIC_ERROR = 0x2C;
+ /** Syntactical errors in packet filter(s). */
+ public static final int FILTER_SYTAX_ERROR = 0x2D;
+ /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
+ public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+ /** Packet Data Protocol (PDP) type IPv4 only allowed. */
+ public static final int ONLY_IPV4_ALLOWED = 0x32; /* no retry */
+ /** Packet Data Protocol (PDP) type IPv6 only allowed. */
+ public static final int ONLY_IPV6_ALLOWED = 0x33; /* no retry */
+ /** Single address bearers only allowed. */
+ public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34;
+ /** EPS Session Management (ESM) information is not received. */
+ public static final int ESM_INFO_NOT_RECEIVED = 0x35;
+ /** PDN connection does not exist. */
+ public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
+ /** Multiple connections to a same PDN is not allowed. */
+ public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+ /** Packet Data Protocol (PDP) */
+ public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+ /** Unsupported APN in current public land mobile network (PLMN). */
+ public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
+ /** Invalid transaction id. */
+ public static final int INVALID_TRANSACTION_ID = 0x51;
+ /** Incorrect message semantic. */
+ public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F;
+ /** Invalid mandatory information. */
+ public static final int INVALID_MANDATORY_INFO = 0x60;
+ /** Unsupported message type. */
+ public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61;
+ /** Message type uncompatible. */
+ public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62;
+ /** Unknown info element. */
+ public static final int UNKNOWN_INFO_ELEMENT = 0x63;
+ /** Conditional Information Element (IE) error. */
+ public static final int CONDITIONAL_IE_ERROR = 0x64;
+ /** Message and protocol state uncompatible. */
+ public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65;
+ /** Protocol errors. */
+ public static final int PROTOCOL_ERRORS = 0x6F; /* no retry */
+ /** APN type conflict. */
+ public static final int APN_TYPE_CONFLICT = 0x70;
+ /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */
+ public static final int INVALID_PCSCF_ADDR = 0x71;
+ /** Internal data call preempt by high priority APN. */
+ public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */
+ public static final int EMM_ACCESS_BARRED = 0x73;
+ /** Emergency interface only. */
+ public static final int EMERGENCY_IFACE_ONLY = 0x74;
+ /** Interface mismatch. */
+ public static final int IFACE_MISMATCH = 0x75;
+ /** Companion interface in use. */
+ public static final int COMPANION_IFACE_IN_USE = 0x76;
+ /** IP address mismatch. */
+ public static final int IP_ADDRESS_MISMATCH = 0x77;
+ public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/
+ public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
+ /** Authentication failure on emergency call. */
+ public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
- OEM_DCFAILCAUSE_1(0x1001),
- OEM_DCFAILCAUSE_2(0x1002),
- OEM_DCFAILCAUSE_3(0x1003),
- OEM_DCFAILCAUSE_4(0x1004),
- OEM_DCFAILCAUSE_5(0x1005),
- OEM_DCFAILCAUSE_6(0x1006),
- OEM_DCFAILCAUSE_7(0x1007),
- OEM_DCFAILCAUSE_8(0x1008),
- OEM_DCFAILCAUSE_9(0x1009),
- OEM_DCFAILCAUSE_10(0x100A),
- OEM_DCFAILCAUSE_11(0x100B),
- OEM_DCFAILCAUSE_12(0x100C),
- OEM_DCFAILCAUSE_13(0x100D),
- OEM_DCFAILCAUSE_14(0x100E),
- OEM_DCFAILCAUSE_15(0x100F),
+ public static final int OEM_DCFAILCAUSE_1 = 0x1001;
+ public static final int OEM_DCFAILCAUSE_2 = 0x1002;
+ public static final int OEM_DCFAILCAUSE_3 = 0x1003;
+ public static final int OEM_DCFAILCAUSE_4 = 0x1004;
+ public static final int OEM_DCFAILCAUSE_5 = 0x1005;
+ public static final int OEM_DCFAILCAUSE_6 = 0x1006;
+ public static final int OEM_DCFAILCAUSE_7 = 0x1007;
+ public static final int OEM_DCFAILCAUSE_8 = 0x1008;
+ public static final int OEM_DCFAILCAUSE_9 = 0x1009;
+ public static final int OEM_DCFAILCAUSE_10 = 0x100A;
+ public static final int OEM_DCFAILCAUSE_11 = 0x100B;
+ public static final int OEM_DCFAILCAUSE_12 = 0x100C;
+ public static final int OEM_DCFAILCAUSE_13 = 0x100D;
+ public static final int OEM_DCFAILCAUSE_14 = 0x100E;
+ public static final int OEM_DCFAILCAUSE_15 = 0x100F;
// Local errors generated by Vendor RIL
// specified in ril.h
- REGISTRATION_FAIL(-1),
- GPRS_REGISTRATION_FAIL(-2),
- SIGNAL_LOST(-3), /* no retry */
- PREF_RADIO_TECH_CHANGED(-4),
- RADIO_POWER_OFF(-5), /* no retry */
- TETHERED_CALL_ACTIVE(-6), /* no retry */
- ERROR_UNSPECIFIED(0xFFFF),
+ public static final int REGISTRATION_FAIL = -1;
+ public static final int GPRS_REGISTRATION_FAIL = -2;
+ public static final int SIGNAL_LOST = -3; /* no retry */
+ public static final int PREF_RADIO_TECH_CHANGED = -4;
+ public static final int RADIO_POWER_OFF = -5; /* no retry */
+ public static final int TETHERED_CALL_ACTIVE = -6; /* no retry */
+ public static final int ERROR_UNSPECIFIED = 0xFFFF;
// Errors generated by the Framework
// specified here
- UNKNOWN(0x10000),
- RADIO_NOT_AVAILABLE(0x10001), /* no retry */
- UNACCEPTABLE_NETWORK_PARAMETER(0x10002), /* no retry */
- CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
- LOST_CONNECTION(0x10004),
- RESET_BY_FRAMEWORK(0x10005);
+ public static final int UNKNOWN = 0x10000;
+ public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */
+ public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */
+ public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+ public static final int LOST_CONNECTION = 0x10004;
+ /** Data was reset by framework. */
+ public static final int RESET_BY_FRAMEWORK = 0x10005;
+
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ OPERATOR_BARRED,
+ NAS_SIGNALLING,
+ LLC_SNDCP,
+ INSUFFICIENT_RESOURCES,
+ MISSING_UNKNOWN_APN,
+ UNKNOWN_PDP_ADDRESS_TYPE,
+ USER_AUTHENTICATION,
+ ACTIVATION_REJECT_GGSN,
+ ACTIVATION_REJECT_UNSPECIFIED,
+ SERVICE_OPTION_NOT_SUPPORTED,
+ SERVICE_OPTION_NOT_SUBSCRIBED,
+ SERVICE_OPTION_OUT_OF_ORDER,
+ NSAPI_IN_USE,
+ REGULAR_DEACTIVATION,
+ QOS_NOT_ACCEPTED,
+ NETWORK_FAILURE,
+ UMTS_REACTIVATION_REQ,
+ FEATURE_NOT_SUPP,
+ TFT_SEMANTIC_ERROR,
+ TFT_SYTAX_ERROR,
+ UNKNOWN_PDP_CONTEXT,
+ FILTER_SEMANTIC_ERROR,
+ FILTER_SYTAX_ERROR,
+ PDP_WITHOUT_ACTIVE_TFT,
+ ONLY_IPV4_ALLOWED,
+ ONLY_IPV6_ALLOWED,
+ ONLY_SINGLE_BEARER_ALLOWED,
+ ESM_INFO_NOT_RECEIVED,
+ PDN_CONN_DOES_NOT_EXIST,
+ MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ INVALID_TRANSACTION_ID,
+ MESSAGE_INCORRECT_SEMANTIC,
+ INVALID_MANDATORY_INFO,
+ MESSAGE_TYPE_UNSUPPORTED,
+ MSG_TYPE_NONCOMPATIBLE_STATE,
+ UNKNOWN_INFO_ELEMENT,
+ CONDITIONAL_IE_ERROR,
+ MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ PROTOCOL_ERRORS, /* no retry */
+ APN_TYPE_CONFLICT,
+ INVALID_PCSCF_ADDR,
+ INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ EMM_ACCESS_BARRED,
+ EMERGENCY_IFACE_ONLY,
+ IFACE_MISMATCH,
+ COMPANION_IFACE_IN_USE,
+ IP_ADDRESS_MISMATCH,
+ IFACE_AND_POL_FAMILY_MISMATCH,
+ EMM_ACCESS_BARRED_INFINITE_RETRY,
+ AUTH_FAILURE_ON_EMERGENCY_CALL,
+ OEM_DCFAILCAUSE_1,
+ OEM_DCFAILCAUSE_2,
+ OEM_DCFAILCAUSE_3,
+ OEM_DCFAILCAUSE_4,
+ OEM_DCFAILCAUSE_5,
+ OEM_DCFAILCAUSE_6,
+ OEM_DCFAILCAUSE_7,
+ OEM_DCFAILCAUSE_8,
+ OEM_DCFAILCAUSE_9,
+ OEM_DCFAILCAUSE_10,
+ OEM_DCFAILCAUSE_11,
+ OEM_DCFAILCAUSE_12,
+ OEM_DCFAILCAUSE_13,
+ OEM_DCFAILCAUSE_14,
+ OEM_DCFAILCAUSE_15,
+ REGISTRATION_FAIL,
+ GPRS_REGISTRATION_FAIL,
+ SIGNAL_LOST,
+ PREF_RADIO_TECH_CHANGED,
+ RADIO_POWER_OFF,
+ TETHERED_CALL_ACTIVE,
+ ERROR_UNSPECIFIED,
+ UNKNOWN,
+ RADIO_NOT_AVAILABLE,
+ UNACCEPTABLE_NETWORK_PARAMETER,
+ CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ LOST_CONNECTION,
+ RESET_BY_FRAMEWORK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FailCause{}
- private final int mErrorCode;
- private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+ private static final Map<Integer, String> sFailCauseMap;
static {
- sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
- for (DataFailCause fc : values()) {
- sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
- }
+ sFailCauseMap = new HashMap<>();
+ sFailCauseMap.put(NONE, "NONE");
+ sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED");
+ sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING");
+ sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP");
+ sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES");
+ sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN");
+ sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE");
+ sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION");
+ sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN");
+ sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED,
+ "ACTIVATION_REJECT_UNSPECIFIED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED,
+ "SERVICE_OPTION_NOT_SUPPORTED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED,
+ "SERVICE_OPTION_NOT_SUBSCRIBED");
+ sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER");
+ sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE");
+ sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION");
+ sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED");
+ sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE");
+ sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ");
+ sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP");
+ sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR");
+ sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR");
+ sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT");
+ sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
+ sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
+ sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+ sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
+ sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
+ sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
+ sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED");
+ sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
+ sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+ sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+ sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ "UNSUPPORTED_APN_IN_CURRENT_PLMN");
+ sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
+ sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC");
+ sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO");
+ sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED");
+ sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE");
+ sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT");
+ sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR");
+ sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE");
+ sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS");
+ sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT");
+ sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR");
+ sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN");
+ sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED");
+ sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY");
+ sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH");
+ sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE");
+ sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH");
+ sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH,
+ "IFACE_AND_POL_FAMILY_MISMATCH");
+ sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY,
+ "EMM_ACCESS_BARRED_INFINITE_RETRY");
+ sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
+ "AUTH_FAILURE_ON_EMERGENCY_CALL");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15");
+ sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL");
+ sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL");
+ sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST");
+ sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED");
+ sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF");
+ sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE");
+ sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED");
+ sFailCauseMap.put(UNKNOWN, "UNKNOWN");
+ sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
+ sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
+ "UNACCEPTABLE_NETWORK_PARAMETER");
+ sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
+ sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+ sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
}
/**
* Map of subId -> set of data call setup permanent failure for the carrier.
*/
- private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+ private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache =
new HashMap<>();
- DataFailCause(int errorCode) {
- mErrorCode = errorCode;
- }
-
- public int getErrorCode() {
- return mErrorCode;
- }
-
/**
* Returns whether or not the fail cause is a failure that requires a modem restart
*
* @param context device context
+ * @param cause data disconnect cause
* @param subId subscription index
* @return true if the fail cause code needs platform to trigger a modem restart.
*/
- public boolean isRadioRestartFailure(Context context, int subId) {
+ public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
+ int subId) {
CarrierConfigManager configManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(subId);
if (b != null) {
- if (this == REGULAR_DEACTIVATION
+ if (cause == REGULAR_DEACTIVATION
&& b.getBoolean(CarrierConfigManager
.KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
// This is for backward compatibility support. We need to continue support this
@@ -170,7 +402,7 @@ public enum DataFailCause {
int[] causeCodes = b.getIntArray(CarrierConfigManager
.KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
if (causeCodes != null) {
- return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+ return Arrays.stream(causeCodes).anyMatch(i -> i == cause);
}
}
}
@@ -178,11 +410,11 @@ public enum DataFailCause {
return false;
}
- public boolean isPermanentFailure(Context context, int subId) {
-
+ public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
+ int subId) {
synchronized (sPermanentFailureCache) {
- HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+ Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId);
// In case of cache miss, we need to look up the settings from carrier config.
if (permanentFailureSet == null) {
@@ -194,11 +426,12 @@ public enum DataFailCause {
if (b != null) {
String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
-
if (permanentFailureStrings != null) {
permanentFailureSet = new HashSet<>();
- for (String failure : permanentFailureStrings) {
- permanentFailureSet.add(DataFailCause.valueOf(failure));
+ for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
+ if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) {
+ permanentFailureSet.add(e.getKey());
+ }
}
}
}
@@ -207,7 +440,7 @@ public enum DataFailCause {
// If we are not able to find the configuration from carrier config, use the default
// ones.
if (permanentFailureSet == null) {
- permanentFailureSet = new HashSet<DataFailCause>() {
+ permanentFailureSet = new HashSet<Integer>() {
{
add(OPERATOR_BARRED);
add(MISSING_UNKNOWN_APN);
@@ -232,28 +465,39 @@ public enum DataFailCause {
sPermanentFailureCache.put(subId, permanentFailureSet);
}
- return permanentFailureSet.contains(this);
+ return permanentFailureSet.contains(failCause);
}
}
- public boolean isEventLoggable() {
- return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
- (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
- (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
- (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
- (this == SERVICE_OPTION_NOT_SUPPORTED) ||
- (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
- (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
- (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
- (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
- (this == UNACCEPTABLE_NETWORK_PARAMETER);
+ public static boolean isEventLoggable(@FailCause int dataFailCause) {
+ return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
+ || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
+ || (dataFailCause == USER_AUTHENTICATION)
+ || (dataFailCause == ACTIVATION_REJECT_GGSN)
+ || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED)
+ || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER)
+ || (dataFailCause == NSAPI_IN_USE)
+ || (dataFailCause == ONLY_IPV4_ALLOWED)
+ || (dataFailCause == ONLY_IPV6_ALLOWED)
+ || (dataFailCause == PROTOCOL_ERRORS)
+ || (dataFailCause == SIGNAL_LOST)
+ || (dataFailCause == RADIO_POWER_OFF)
+ || (dataFailCause == TETHERED_CALL_ACTIVE)
+ || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
+ }
+
+ public static String toString(@FailCause int dataFailCause) {
+ int cause = getFailCause(dataFailCause);
+ return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
}
- public static DataFailCause fromInt(int errorCode) {
- DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
- if (fc == null) {
- fc = UNKNOWN;
+ public static int getFailCause(@FailCause int failCause) {
+ if (sFailCauseMap.containsKey(failCause)) {
+ return failCause;
+ } else {
+ return UNKNOWN;
}
- return fc;
}
}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index c6887ab93109..f53cb8224706 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -16,12 +16,16 @@
package android.telephony;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
/**
- * Contains disconnect call causes generated by the framework and the RIL.
+ * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
+ * generic {@link android.telecom.DisconnectCause} object.
+ *
* @hide
*/
+@SystemApi
public class DisconnectCause {
/** The disconnect cause is not valid (Not received a disconnect cause) */
@@ -101,8 +105,8 @@ public class DisconnectCause {
/** Unknown error or not specified */
public static final int ERROR_UNSPECIFIED = 36;
/**
- * Only emergency numbers are allowed, but we tried to dial
- * a non-emergency number.
+ * Only emergency numbers are allowed, but we tried to dial a non-emergency number.
+ * @hide
*/
// TODO: This should be the same as NOT_EMERGENCY
public static final int EMERGENCY_ONLY = 37;
@@ -115,8 +119,7 @@ public class DisconnectCause {
*/
public static final int DIALED_MMI = 39;
/**
- * We tried to call a voicemail: URI but the device has no
- * voicemail number configured.
+ * We tried to call a voicemail: URI but the device has no voicemail number configured.
*/
public static final int VOICEMAIL_NUMBER_MISSING = 40;
/**
@@ -129,6 +132,8 @@ public class DisconnectCause {
* needs to be triggered by a *disconnect* event, rather than when
* the InCallScreen first comes to the foreground. For now we use
* the needToShowCallLostDialog field for this (see below.)
+ *
+ * @hide
*/
public static final int CDMA_CALL_LOST = 41;
/**
@@ -169,62 +174,52 @@ public class DisconnectCause {
/**
* Stk Call Control modified DIAL request to USSD request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_USSD = 46;
/**
* Stk Call Control modified DIAL request to SS request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_SS = 47;
/**
* Stk Call Control modified DIAL request to DIAL with modified data.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_DIAL = 48;
/**
* The call was terminated because CDMA phone service and roaming have already been activated.
- * {@hide}
*/
public static final int CDMA_ALREADY_ACTIVATED = 49;
/**
* The call was terminated because it is not possible to place a video call while TTY is
* enabled.
- * {@hide}
*/
public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
/**
* The call was terminated because it was pulled to another device.
- * {@hide}
*/
public static final int CALL_PULLED = 51;
/**
* The call was terminated because it was answered on another device.
- * {@hide}
*/
public static final int ANSWERED_ELSEWHERE = 52;
/**
* The call was terminated because the maximum allowable number of calls has been reached.
- * {@hide}
*/
public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
/**
* The call was terminated because cellular data has been disabled.
* Used when in a video call and the user disables cellular data via the settings.
- * {@hide}
*/
public static final int DATA_DISABLED = 54;
/**
* The call was terminated because the data policy has disabled cellular data.
* Used when in a video call and the user has exceeded the device data limit.
- * {@hide}
*/
public static final int DATA_LIMIT_REACHED = 55;
@@ -237,7 +232,6 @@ public class DisconnectCause {
/**
* The network does not accept the emergency call request because IMEI was used as
* identification and this cability is not supported by the network.
- * {@hide}
*/
public static final int IMEI_NOT_ACCEPTED = 58;
@@ -249,7 +243,6 @@ public class DisconnectCause {
/**
* The call has failed because of access class barring.
- * {@hide}
*/
public static final int IMS_ACCESS_BLOCKED = 60;
@@ -265,51 +258,43 @@ public class DisconnectCause {
/**
* Emergency call failed with a temporary fail cause and can be redialed on this slot.
- * {@hide}
*/
public static final int EMERGENCY_TEMP_FAILURE = 63;
/**
* Emergency call failed with a permanent fail cause and should not be redialed on this
- * slot.
- * {@hide}
+ * slot.
*/
public static final int EMERGENCY_PERM_FAILURE = 64;
/**
* This cause is used to report a normal event only when no other cause in the normal class
* applies.
- * {@hide}
*/
public static final int NORMAL_UNSPECIFIED = 65;
/**
* Stk Call Control modified DIAL request to video DIAL request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
/**
* Stk Call Control modified Video DIAL request to SS request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
/**
* Stk Call Control modified Video DIAL request to USSD request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
/**
* Stk Call Control modified Video DIAL request to DIAL request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
/**
* Stk Call Control modified Video DIAL request to Video DIAL request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
@@ -359,7 +344,10 @@ public class DisconnectCause {
// Do nothing.
}
- /** Returns descriptive string for the specified disconnect cause. */
+ /**
+ * Returns descriptive string for the specified disconnect cause.
+ * @hide
+ */
@UnsupportedAppUsage
public static String toString(int cause) {
switch (cause) {
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0df0dafbe1dd..9317aa73ffc2 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -173,14 +173,14 @@ public class PhoneStateListener {
public static final int LISTEN_CELL_INFO = 0x00000400;
/**
- * Listen for precise changes and fails to the device calls (cellular).
+ * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
* {@more}
* Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE}
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
/**
@@ -320,6 +320,18 @@ public class PhoneStateListener {
*/
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
+ /**
+ * Listen for call disconnect causes which contains {@link DisconnectCause} and
+ * {@link PreciseDisconnectCause}.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -530,15 +542,27 @@ public class PhoneStateListener {
/**
* Callback invoked when precise device call state changes.
- *
+ * @param callState {@link PreciseCallState}
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void onPreciseCallStateChanged(PreciseCallState callState) {
// default implementation empty
}
/**
+ * Callback invoked when call disconnect cause changes.
+ * @param disconnectCause {@link DisconnectCause}.
+ * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when data connection state changes with precise information.
*
* @hide
@@ -799,6 +823,15 @@ public class PhoneStateListener {
() -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
}
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+ disconnectCause, preciseDisconnectCause)));
+ }
+
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index ed5c26ac5cf2..59f3e1f0e7f7 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -16,29 +16,51 @@
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.DisconnectCause;
import android.telephony.PreciseDisconnectCause;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
/**
- * Contains precise call state and call fail causes generated by the
- * framework and the RIL.
+ * Contains precise call states.
*
* The following call information is included in returned PreciseCallState:
*
* <ul>
- * <li>Ringing call state.
- * <li>Foreground call state.
- * <li>Background call state.
- * <li>Disconnect cause; generated by the framework.
- * <li>Precise disconnect cause; generated by the RIL.
+ * <li>Precise ringing call state.
+ * <li>Precise foreground call state.
+ * <li>Precise background call state.
* </ul>
*
+ * @see android.telephony.TelephonyManager.CallState which contains generic call states.
+ *
* @hide
*/
-public class PreciseCallState implements Parcelable {
+@SystemApi
+public final class PreciseCallState implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+ value = {
+ PRECISE_CALL_STATE_NOT_VALID,
+ PRECISE_CALL_STATE_IDLE,
+ PRECISE_CALL_STATE_ACTIVE,
+ PRECISE_CALL_STATE_HOLDING,
+ PRECISE_CALL_STATE_DIALING,
+ PRECISE_CALL_STATE_ALERTING,
+ PRECISE_CALL_STATE_INCOMING,
+ PRECISE_CALL_STATE_WAITING,
+ PRECISE_CALL_STATE_DISCONNECTED,
+ PRECISE_CALL_STATE_DISCONNECTING})
+ public @interface State {}
/** Call state is not valid (Not received a call state). */
public static final int PRECISE_CALL_STATE_NOT_VALID = -1;
@@ -61,9 +83,9 @@ public class PreciseCallState implements Parcelable {
/** Call state: Disconnecting. */
public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
- private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
- private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
- private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
@@ -73,8 +95,9 @@ public class PreciseCallState implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
- int disconnectCause, int preciseDisconnectCause) {
+ public PreciseCallState(@State int ringingCall, @State int foregroundCall,
+ @State int backgroundCall, int disconnectCause,
+ int preciseDisconnectCause) {
mRingingCallState = ringingCall;
mForegroundCallState = foregroundCall;
mBackgroundCallState = backgroundCall;
@@ -92,6 +115,8 @@ public class PreciseCallState implements Parcelable {
/**
* Construct a PreciseCallState object from the given parcel.
+ *
+ * @hide
*/
private PreciseCallState(Parcel in) {
mRingingCallState = in.readInt();
@@ -102,59 +127,23 @@ public class PreciseCallState implements Parcelable {
}
/**
- * Get precise ringing call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise ringing call state.
*/
- @UnsupportedAppUsage
- public int getRingingCallState() {
+ public @State int getRingingCallState() {
return mRingingCallState;
}
/**
- * Get precise foreground call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise foreground call state.
*/
- @UnsupportedAppUsage
- public int getForegroundCallState() {
+ public @State int getForegroundCallState() {
return mForegroundCallState;
}
/**
- * Get precise background call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise background call state.
*/
- @UnsupportedAppUsage
- public int getBackgroundCallState() {
+ public @State int getBackgroundCallState() {
return mBackgroundCallState;
}
@@ -199,6 +188,11 @@ public class PreciseCallState implements Parcelable {
* @see DisconnectCause#CDMA_NOT_EMERGENCY
* @see DisconnectCause#CDMA_ACCESS_BLOCKED
* @see DisconnectCause#ERROR_UNSPECIFIED
+ *
+ * TODO: remove disconnect cause from preciseCallState as there is no link between random
+ * connection disconnect cause with foreground, background or ringing call.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public int getDisconnectCause() {
@@ -238,6 +232,11 @@ public class PreciseCallState implements Parcelable {
* @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
* @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
* @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+ *
+ * TODO: remove precise disconnect cause from preciseCallState as there is no link between
+ * random connection disconnect cause with foreground, background or ringing call.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public int getPreciseDisconnectCause() {
@@ -272,14 +271,8 @@ public class PreciseCallState implements Parcelable {
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mRingingCallState;
- result = prime * result + mForegroundCallState;
- result = prime * result + mBackgroundCallState;
- result = prime * result + mDisconnectCause;
- result = prime * result + mPreciseDisconnectCause;
- return result;
+ return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState,
+ mDisconnectCause, mPreciseDisconnectCause);
}
@Override
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2acaf34dbb30..af88748af9e6 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -16,279 +16,329 @@
package android.telephony;
+import android.annotation.SystemApi;
+
/**
- * Contains precise disconnect call causes generated by the
- * framework and the RIL.
- *
+ * Contains precise disconnect call causes generated by the framework and the RIL.
* @hide
*/
+@SystemApi
public class PreciseDisconnectCause {
- /** The disconnect cause is not valid (Not received a disconnect cause)*/
+ /** The disconnect cause is not valid (Not received a disconnect cause).*/
public static final int NOT_VALID = -1;
- /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+ /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */
public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
/**
* The destination cannot be reached because the number, although valid,
- * is not currently assigned
+ * is not currently assigned.
*/
public static final int UNOBTAINABLE_NUMBER = 1;
- /** The user cannot be reached because the network through which the call has been
- * routed does not serve the destination desired
+ /**
+ * The user cannot be reached because the network through which the call has been routed does
+ * not serve the destination desired.
*/
public static final int NO_ROUTE_TO_DESTINATION = 3;
- /** The channel most recently identified is not acceptable to the sending entity for
- * use in this call
+ /**
+ * The channel most recently identified is not acceptable to the sending entity for use in this
+ * call.
*/
public static final int CHANNEL_UNACCEPTABLE = 6;
- /** The MS has tried to access a service that the MS's network operator or service
- * provider is not prepared to allow
+ /**
+ * The mobile station (MS) has tried to access a service that the MS's network operator or
+ * service provider is not prepared to allow.
*/
public static final int OPERATOR_DETERMINED_BARRING = 8;
- /** One of the users involved in the call has requested that the call is cleared */
+ /** One of the users involved in the call has requested that the call is cleared. */
public static final int NORMAL = 16;
- /** The called user is unable to accept another call */
+ /** The called user is unable to accept another call. */
public static final int BUSY = 17;
- /** The user does not respond to a call establishment message with either an alerting
- * or connect indication within the prescribed period of time allocated
+ /**
+ * The user does not respond to a call establishment message with either an alerting or connect
+ * indication within the prescribed period of time allocated.
*/
public static final int NO_USER_RESPONDING = 18;
- /** The user has provided an alerting indication but has not provided a connect
- * indication within a prescribed period of time
+ /**
+ * The user has provided an alerting indication but has not provided a connect indication
+ * within a prescribed period of time.
*/
public static final int NO_ANSWER_FROM_USER = 19;
- /** The equipment sending this cause does not wish to accept this call */
+ /** The equipment sending this cause does not wish to accept this call. */
public static final int CALL_REJECTED = 21;
- /** The called number is no longer assigned */
+ /** The called number is no longer assigned. */
public static final int NUMBER_CHANGED = 22;
- /** This cause is returned to the network when a mobile station clears an active
- * call which is being pre-empted by another call with higher precedence
+ /**
+ * This cause is returned to the network when a mobile station clears an active call which is
+ * being pre-empted by another call with higher precedence.
*/
public static final int PREEMPTION = 25;
- /** The destination indicated by the mobile station cannot be reached because
- * the interface to the destination is not functioning correctly
+ /**
+ * The destination indicated by the mobile station cannot be reached because the interface to
+ * the destination is not functioning correctly.
*/
public static final int DESTINATION_OUT_OF_ORDER = 27;
- /** The called party number is not a valid format or is not complete */
+ /** The called party number is not a valid format or is not complete. */
public static final int INVALID_NUMBER_FORMAT = 28;
- /** The facility requested by user can not be provided by the network */
+ /** The facility requested by user can not be provided by the network. */
public static final int FACILITY_REJECTED = 29;
- /** Provided in response to a STATUS ENQUIRY message */
+ /** Provided in response to a STATUS ENQUIRY message. */
public static final int STATUS_ENQUIRY = 30;
- /** Reports a normal disconnect only when no other normal cause applies */
+ /** Reports a normal disconnect only when no other normal cause applies. */
public static final int NORMAL_UNSPECIFIED = 31;
- /** There is no channel presently available to handle the call */
+ /** There is no channel presently available to handle the call. */
public static final int NO_CIRCUIT_AVAIL = 34;
- /** The network is not functioning correctly and that the condition is likely
- * to last a relatively long period of time
+ /**
+ * The network is not functioning correctly and that the condition is likely to last a
+ * relatively long period of time.
*/
public static final int NETWORK_OUT_OF_ORDER = 38;
/**
- * The network is not functioning correctly and the condition is not likely to last
- * a long period of time
+ * The network is not functioning correctly and the condition is not likely to last a long
+ * period of time.
*/
public static final int TEMPORARY_FAILURE = 41;
- /** The switching equipment is experiencing a period of high traffic */
+ /** The switching equipment is experiencing a period of high traffic. */
public static final int SWITCHING_CONGESTION = 42;
- /** The network could not deliver access information to the remote user as requested */
+ /** The network could not deliver access information to the remote user as requested. */
public static final int ACCESS_INFORMATION_DISCARDED = 43;
- /** The channel cannot be provided */
+ /** The channel cannot be provided. */
public static final int CHANNEL_NOT_AVAIL = 44;
- /** This cause is used to report a resource unavailable event only when no other
- * cause in the resource unavailable class applies
+ /**
+ * This cause is used to report a resource unavailable event only when no other cause in the
+ * resource unavailable class applies.
*/
public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47;
- /** The requested quality of service (ITU-T X.213) cannot be provided */
+ /** The requested quality of service (ITU-T X.213) cannot be provided. */
public static final int QOS_NOT_AVAIL = 49;
- /** The facility could not be provided by the network because the user has no
- * complete subscription
+ /**
+ * The facility could not be provided by the network because the user has no complete
+ * subscription.
*/
public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
- /** Incoming calls are not allowed within this CUG */
+ /** Incoming calls are not allowed within this calling user group (CUG). */
public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55;
- /** The mobile station is not authorized to use bearer capability requested */
+ /** The mobile station is not authorized to use bearer capability requested. */
public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
- /** The requested bearer capability is not available at this time */
+ /** The requested bearer capability is not available at this time. */
public static final int BEARER_NOT_AVAIL = 58;
- /** The service option is not availble at this time */
+ /** The service option is not availble at this time. */
public static final int SERVICE_OPTION_NOT_AVAILABLE = 63;
- /** The equipment sending this cause does not support the bearer capability requested */
+ /** The equipment sending this cause does not support the bearer capability requested. */
public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
- /** The call clearing is due to ACM being greater than or equal to ACMmax */
+ /** The call clearing is due to ACM being greater than or equal to ACMmax. */
public static final int ACM_LIMIT_EXCEEDED = 68;
- /** The equipment sending this cause does not support the requested facility */
+ /** The equipment sending this cause does not support the requested facility. */
public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
- /** The equipment sending this cause only supports the restricted version of
- * the requested bearer capability
+ /**
+ * The equipment sending this cause only supports the restricted version of the requested bearer
+ * capability.
*/
public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70;
- /** The service requested is not implemented at network */
+ /** The service requested is not implemented at network. */
public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79;
- /** The equipment sending this cause has received a message with a transaction identifier
- * which is not currently in use on the MS-network interface
+ /**
+ * The equipment sending this cause has received a message with a transaction identifier
+ * which is not currently in use on the mobile station network interface.
*/
public static final int INVALID_TRANSACTION_IDENTIFIER = 81;
- /** The called user for the incoming CUG call is not a member of the specified CUG */
+ /**
+ * The called user for the incoming CUG call is not a member of the specified calling user
+ * group (CUG).
+ */
public static final int USER_NOT_MEMBER_OF_CUG = 87;
- /** The equipment sending this cause has received a request which can't be accomodated */
+ /** The equipment sending this cause has received a request which can't be accomodated. */
public static final int INCOMPATIBLE_DESTINATION = 88;
- /** This cause is used to report receipt of a message with semantically incorrect contents */
+ /** This cause is used to report receipt of a message with semantically incorrect contents. */
public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95;
- /** The equipment sending this cause has received a message with a non-semantical
- * mandatory IE error
+ /**
+ * The equipment sending this cause has received a message with a non-semantical mandatory
+ * information element (IE) error.
*/
public static final int INVALID_MANDATORY_INFORMATION = 96;
- /** This is sent in response to a message which is not defined, or defined but not
- * implemented by the equipment sending this cause
+ /**
+ * This is sent in response to a message which is not defined, or defined but not implemented
+ * by the equipment sending this cause.
*/
public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97;
- /** The equipment sending this cause has received a message not compatible with the
- * protocol state
+ /**
+ * The equipment sending this cause has received a message not compatible with the protocol
+ * state.
*/
public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98;
- /** The equipment sending this cause has received a message which includes information
- * elements not recognized because its identifier is not defined or it is defined but not
- * implemented by the equipment sending the cause
+ /**
+ * The equipment sending this cause has received a message which includes information
+ * elements not recognized because its identifier is not defined or it is defined but not
+ * implemented by the equipment sending the cause.
*/
public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99;
- /** The equipment sending this cause has received a message with conditional IE errors */
+ /** The equipment sending this cause has received a message with conditional IE errors. */
public static final int CONDITIONAL_IE_ERROR = 100;
- /** The message has been received which is incompatible with the protocol state */
+ /** The message has been received which is incompatible with the protocol state. */
public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101;
- /** The procedure has been initiated by the expiry of a timer in association with
- * 3GPP TS 24.008 error handling procedures
+ /**
+ * The procedure has been initiated by the expiry of a timer in association with
+ * 3GPP TS 24.008 error handling procedures.
*/
public static final int RECOVERY_ON_TIMER_EXPIRED = 102;
- /** This protocol error event is reported only when no other cause in the protocol
- * error class applies
+ /**
+ * This protocol error event is reported only when no other cause in the protocol error class
+ * applies.
*/
public static final int PROTOCOL_ERROR_UNSPECIFIED = 111;
- /** interworking with a network which does not provide causes for actions it takes
- * thus, the precise cause for a message which is being sent cannot be ascertained
+ /**
+ * Interworking with a network which does not provide causes for actions it takes thus, the
+ * precise cause for a message which is being sent cannot be ascertained.
*/
public static final int INTERWORKING_UNSPECIFIED = 127;
- /** The call is restricted */
+ /** The call is restricted. */
public static final int CALL_BARRED = 240;
- /** The call is blocked by the Fixed Dialing Number list */
+ /** The call is blocked by the Fixed Dialing Number list. */
public static final int FDN_BLOCKED = 241;
- /** The given IMSI is not known at the VLR */
- /** TS 24.008 cause 4 */
+ /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */
public static final int IMSI_UNKNOWN_IN_VLR = 242;
/**
* The network does not accept emergency call establishment using an IMEI or not accept attach
- * procedure for emergency services using an IMEI
+ * procedure for emergency services using an IMEI.
*/
public static final int IMEI_NOT_ACCEPTED = 243;
- /** The call cannot be established because RADIO is OFF */
+ /** The call cannot be established because RADIO is OFF. */
public static final int RADIO_OFF = 247;
- /** The call cannot be established because of no cell coverage */
+ /** The call cannot be established because of no cell coverage. */
public static final int OUT_OF_SRV = 248;
- /** The call cannot be established because of no valid SIM */
+ /** The call cannot be established because of no valid SIM. */
public static final int NO_VALID_SIM = 249;
- /** The call is dropped or failed internally by modem */
+ /** The call is dropped or failed internally by modem. */
public static final int RADIO_INTERNAL_ERROR = 250;
- /** Call failed because of UE timer expired while waiting for a response from network */
+ /** Call failed because of UE timer expired while waiting for a response from network. */
public static final int NETWORK_RESP_TIMEOUT = 251;
- /** Call failed because of a network reject */
+ /** Call failed because of a network reject. */
public static final int NETWORK_REJECT = 252;
- /** Call failed because of radio access failure. ex. RACH failure */
+ /** Call failed because of radio access failure. ex. RACH failure. */
public static final int RADIO_ACCESS_FAILURE = 253;
- /** Call failed/dropped because of a RLF */
+ /** Call failed/dropped because of a Radio Link Failure (RLF). */
public static final int RADIO_LINK_FAILURE = 254;
- /** Call failed/dropped because of radio link lost */
+ /** Call failed/dropped because of radio link lost. */
public static final int RADIO_LINK_LOST = 255;
- /** Call failed because of a radio uplink issue */
+ /** Call failed because of a radio uplink issue. */
public static final int RADIO_UPLINK_FAILURE = 256;
- /** Call failed because of a RRC connection setup failure */
+ /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */
public static final int RADIO_SETUP_FAILURE = 257;
- /** Call failed/dropped because of RRC connection release from NW */
+ /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */
public static final int RADIO_RELEASE_NORMAL = 258;
- /** Call failed/dropped because of RRC abnormally released by modem/network */
+ /**
+ * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by
+ * modem/network.
+ */
public static final int RADIO_RELEASE_ABNORMAL = 259;
- /** Call setup failed because of access class barring */
+ /** Call setup failed because of access class barring. */
public static final int ACCESS_CLASS_BLOCKED = 260;
- /** Call failed/dropped because of a network detach */
+ /** Call failed/dropped because of a network detach. */
public static final int NETWORK_DETACH = 261;
- /** MS is locked until next power cycle */
+ /** Mobile station (MS) is locked until next power cycle. */
public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
- /** Drop call*/
+ /** Drop call. */
public static final int CDMA_DROP = 1001;
- /** INTERCEPT order received, MS state idle entered */
+ /** INTERCEPT order received, Mobile station (MS) state idle entered. */
public static final int CDMA_INTERCEPT = 1002;
- /** MS has been redirected, call is cancelled */
+ /** Mobile station (MS) has been redirected, call is cancelled. */
public static final int CDMA_REORDER = 1003;
- /** Service option rejection */
+ /** Service option rejection. */
public static final int CDMA_SO_REJECT = 1004;
- /** Requested service is rejected, retry delay is set */
+ /** Requested service is rejected, retry delay is set. */
public static final int CDMA_RETRY_ORDER = 1005;
- /** Unable to obtain access to the CDMA system */
+ /** Unable to obtain access to the CDMA system. */
public static final int CDMA_ACCESS_FAILURE = 1006;
- /** Not a preempted call */
+ /** Not a preempted call. */
public static final int CDMA_PREEMPTED = 1007;
- /** Not an emergency call */
+ /** Not an emergency call. */
public static final int CDMA_NOT_EMERGENCY = 1008;
- /** Access Blocked by CDMA network */
+ /** Access Blocked by CDMA network. */
public static final int CDMA_ACCESS_BLOCKED = 1009;
/** Mapped from ImsReasonInfo */
+ // TODO: remove ImsReasonInfo from preciseDisconnectCause
/* The passed argument is an invalid */
+ /** @hide */
public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
// The operation is invoked in invalid call state
+ /** @hide */
public static final int LOCAL_ILLEGAL_STATE = 1201;
// IMS service internal error
+ /** @hide */
public static final int LOCAL_INTERNAL_ERROR = 1202;
// IMS service goes down (service connection is lost)
+ /** @hide */
public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
// No pending incoming call exists
+ /** @hide */
public static final int LOCAL_NO_PENDING_CALL = 1204;
// Service unavailable; by power off
+ /** @hide */
public static final int LOCAL_POWER_OFF = 1205;
// Service unavailable; by low battery
+ /** @hide */
public static final int LOCAL_LOW_BATTERY = 1206;
// Service unavailable; by out of service (data service state)
+ /** @hide */
public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
/* Service unavailable; by no LTE coverage
* (VoLTE is not supported even though IMS is registered)
*/
+ /** @hide */
public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
/** Service unavailable; by located in roaming area */
+ /** @hide */
public static final int LOCAL_NETWORK_ROAMING = 1209;
/** Service unavailable; by IP changed */
+ /** @hide */
public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
/** Service unavailable; other */
+ /** @hide */
public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
/* Service unavailable; IMS connection is lost (IMS is not registered) */
+ /** @hide */
public static final int LOCAL_NOT_REGISTERED = 1212;
/** Max call exceeded */
+ /** @hide */
public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
/** Call decline */
+ /** @hide */
public static final int LOCAL_CALL_DECLINE = 1214;
/** SRVCC is in progress */
+ /** @hide */
public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
/** Resource reservation is failed (QoS precondition) */
+ /** @hide */
public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
/** Retry CS call; VoLTE service can't be provided by the network or remote end
* Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ * @hide
*/
public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
/** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+ /** @hide */
public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
/** IMS call is already terminated (in TERMINATED state) */
+ /** @hide */
public static final int LOCAL_CALL_TERMINATED = 1219;
/** Handover not feasible */
+ /** @hide */
public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
/** 1xx waiting timer is expired after sending INVITE request (MO only) */
+ /** @hide */
public static final int TIMEOUT_1XX_WAITING = 1221;
/** User no answer during call setup operation (MO/MT)
* MO : 200 OK to INVITE request is not received,
* MT : No action from user after alerting the call
+ * @hide
*/
public static final int TIMEOUT_NO_ANSWER = 1222;
/** User no answer during call update operation (MO/MT)
* MO : 200 OK to re-INVITE request is not received,
* MT : No action from user after alerting the call
+ * @hide
*/
public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
@@ -296,102 +346,142 @@ public class PreciseDisconnectCause {
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
/** SIP request is redirected */
+ /** @hide */
public static final int SIP_REDIRECTED = 1300;
/** 4xx responses */
/** 400 : Bad Request */
+ /** @hide */
public static final int SIP_BAD_REQUEST = 1310;
/** 403 : Forbidden */
+ /** @hide */
public static final int SIP_FORBIDDEN = 1311;
/** 404 : Not Found */
+ /** @hide */
public static final int SIP_NOT_FOUND = 1312;
/** 415 : Unsupported Media Type
* 416 : Unsupported URI Scheme
* 420 : Bad Extension
*/
+ /** @hide */
public static final int SIP_NOT_SUPPORTED = 1313;
/** 408 : Request Timeout */
+ /** @hide */
public static final int SIP_REQUEST_TIMEOUT = 1314;
/** 480 : Temporarily Unavailable */
+ /** @hide */
public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
/** 484 : Address Incomplete */
+ /** @hide */
public static final int SIP_BAD_ADDRESS = 1316;
/** 486 : Busy Here
* 600 : Busy Everywhere
*/
+ /** @hide */
public static final int SIP_BUSY = 1317;
/** 487 : Request Terminated */
+ /** @hide */
public static final int SIP_REQUEST_CANCELLED = 1318;
/** 406 : Not Acceptable
* 488 : Not Acceptable Here
* 606 : Not Acceptable
*/
+ /** @hide */
public static final int SIP_NOT_ACCEPTABLE = 1319;
/** 410 : Gone
* 604 : Does Not Exist Anywhere
*/
+ /** @hide */
public static final int SIP_NOT_REACHABLE = 1320;
/** Others */
+ /** @hide */
public static final int SIP_CLIENT_ERROR = 1321;
/** 481 : Transaction Does Not Exist */
+ /** @hide */
public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322;
/** 5xx responses
* 501 : Server Internal Error
*/
+ /** @hide */
public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
/** 503 : Service Unavailable */
+ /** @hide */
public static final int SIP_SERVICE_UNAVAILABLE = 1331;
/** 504 : Server Time-out */
+ /** @hide */
public static final int SIP_SERVER_TIMEOUT = 1332;
/** Others */
+ /** @hide */
public static final int SIP_SERVER_ERROR = 1333;
/** 6xx responses
* 603 : Decline
*/
+ /** @hide */
public static final int SIP_USER_REJECTED = 1340;
/** Others */
+ /** @hide */
public static final int SIP_GLOBAL_ERROR = 1341;
/** Emergency failure */
+ /** @hide */
public static final int EMERGENCY_TEMP_FAILURE = 1342;
+ /** @hide */
public static final int EMERGENCY_PERM_FAILURE = 1343;
/** Media resource initialization failed */
+ /** @hide */
public static final int MEDIA_INIT_FAILED = 1400;
/** RTP timeout (no audio / video traffic in the session) */
+ /** @hide */
public static final int MEDIA_NO_DATA = 1401;
/** Media is not supported; so dropped the call */
+ /** @hide */
public static final int MEDIA_NOT_ACCEPTABLE = 1402;
/** Unknown media related errors */
+ /** @hide */
public static final int MEDIA_UNSPECIFIED = 1403;
/** User triggers the call end */
+ /** @hide */
public static final int USER_TERMINATED = 1500;
/** No action while an incoming call is ringing */
+ /** @hide */
public static final int USER_NOANSWER = 1501;
/** User ignores an incoming call */
+ /** @hide */
public static final int USER_IGNORE = 1502;
/** User declines an incoming call */
+ /** @hide */
public static final int USER_DECLINE = 1503;
/** Device declines/ends a call due to low battery */
+ /** @hide */
public static final int LOW_BATTERY = 1504;
/** Device declines call due to blacklisted call ID */
+ /** @hide */
public static final int BLACKLISTED_CALL_ID = 1505;
/** The call is terminated by the network or remote user */
+ /** @hide */
public static final int USER_TERMINATED_BY_REMOTE = 1510;
/**
* UT
*/
+ /** @hide */
public static final int UT_NOT_SUPPORTED = 1800;
+ /** @hide */
public static final int UT_SERVICE_UNAVAILABLE = 1801;
+ /** @hide */
public static final int UT_OPERATION_NOT_ALLOWED = 1802;
+ /** @hide */
public static final int UT_NETWORK_ERROR = 1803;
+ /** @hide */
public static final int UT_CB_PASSWORD_MISMATCH = 1804;
/**
* ECBM
+ * @hide
*/
public static final int ECBM_NOT_SUPPORTED = 1900;
/**
* Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ * @hide
*/
public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
@@ -405,56 +495,68 @@ public class PreciseDisconnectCause {
* active wifi call and at the edge of coverage and there is no qualified LTE network available
* to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
* code is received as part of the handover message.
+ * @hide
*/
public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
/**
* MT call has ended due to a release from the network
* because the call was answered elsewhere
+ * @hide
*/
public static final int ANSWERED_ELSEWHERE = 2100;
/**
* For MultiEndpoint - Call Pull request has failed
+ * @hide
*/
public static final int CALL_PULL_OUT_OF_SYNC = 2101;
/**
* For MultiEndpoint - Call has been pulled from primary to secondary
+ * @hide
*/
public static final int CALL_PULLED = 2102;
/**
* Supplementary services (HOLD/RESUME) failure error codes.
* Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ * @hide
*/
public static final int SUPP_SVC_FAILED = 2300;
+ /** @hide */
public static final int SUPP_SVC_CANCELLED = 2301;
+ /** @hide */
public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
/**
* DPD Procedure received no response or send failed
+ * @hide
*/
public static final int IWLAN_DPD_FAILURE = 2400;
/**
* Establishment of the ePDG Tunnel Failed
+ * @hide
*/
public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
/**
* Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ * @hide
*/
public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
/**
* Connection to the packet gateway is lost
+ * @hide
*/
public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
/**
* The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
* where the number of calls across all connected devices has reached the maximum.
+ * @hide
*/
public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
@@ -462,21 +564,25 @@ public class PreciseDisconnectCause {
* Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
* declined the call. Used in a multi-endpoint scenario where a remote device declined an
* incoming call.
+ * @hide
*/
public static final int REMOTE_CALL_DECLINE = 2504;
/**
* Indicates the call was disconnected due to the user reaching their data limit.
+ * @hide
*/
public static final int DATA_LIMIT_REACHED = 2505;
/**
* Indicates the call was disconnected due to the user disabling cellular data.
+ * @hide
*/
public static final int DATA_DISABLED = 2506;
/**
* Indicates a call was disconnected due to loss of wifi signal.
+ * @hide
*/
public static final int WIFI_LOST = 2507;
@@ -499,7 +605,7 @@ public class PreciseDisconnectCause {
public static final int OEM_CAUSE_14 = 0xf00e;
public static final int OEM_CAUSE_15 = 0xf00f;
- /** Disconnected due to unspecified reasons */
+ /** Disconnected due to unspecified reasons. */
public static final int ERROR_UNSPECIFIED = 0xffff;
/** Private constructor to avoid class instantiation. */
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a7e8e8a95ea2..a1e8b199d2a0 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -138,10 +138,15 @@ public class SubscriptionInfo implements Parcelable {
private UiccAccessRule[] mAccessRules;
/**
- * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
- * for an eUICC card.
+ * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+ * EID for an eUICC card.
*/
- private String mCardId;
+ private String mCardString;
+
+ /**
+ * The card ID of the SIM card. This maps uniquely to the card string.
+ */
+ private int mCardId;
/**
* Whether the subscription is opportunistic.
@@ -174,9 +179,9 @@ public class SubscriptionInfo implements Parcelable {
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId) {
+ @Nullable UiccAccessRule[] accessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
}
@@ -186,20 +191,22 @@ public class SubscriptionInfo implements Parcelable {
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
+ @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
@Nullable String groupUUID, boolean isMetered, int carrierId) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
isOpportunistic, groupUUID, isMetered, false, carrierId);
}
+
/**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
- @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled, int carrierid) {
+ @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
+ boolean isGroupDisabled, int carrierid) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -215,6 +222,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
this.mAccessRules = accessRules;
+ this.mCardString = cardString;
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
this.mGroupUUID = groupUUID;
@@ -523,10 +531,21 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the ID of the SIM card which contains the subscription.
+ * @return the card string of the SIM card which contains the subscription. The card string is
+ * the ICCID for UICCs or the EID for eUICCs.
+ * @hide
+ * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
+ */
+ public String getCardString() {
+ return this.mCardString;
+ }
+
+ /**
+ * @return the cardId of the SIM card which contains the subscription.
* @hide
*/
- public String getCardId() {
+ @SystemApi
+ public int getCardId() {
return this.mCardId;
}
@@ -564,7 +583,8 @@ public class SubscriptionInfo implements Parcelable {
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
- String cardId = source.readString();
+ String cardString = source.readString();
+ int cardId = source.readInt();
boolean isOpportunistic = source.readBoolean();
String groupUUID = source.readString();
boolean isMetered = source.readBoolean();
@@ -573,8 +593,8 @@ public class SubscriptionInfo implements Parcelable {
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered,
- isGroupDisabled, carrierid);
+ isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
+ isMetered, isGroupDisabled, carrierid);
}
@Override
@@ -600,7 +620,8 @@ public class SubscriptionInfo implements Parcelable {
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mAccessRules, flags);
- dest.writeString(mCardId);
+ dest.writeString(mCardString);
+ dest.writeInt(mCardId);
dest.writeBoolean(mIsOpportunistic);
dest.writeString(mGroupUUID);
dest.writeBoolean(mIsMetered);
@@ -631,7 +652,7 @@ public class SubscriptionInfo implements Parcelable {
@Override
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
- String cardIdToPrint = givePrintableIccid(mCardId);
+ String cardStringToPrint = givePrintableIccid(mCardString);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " carrierId=" + mCarrierId + " displayName=" + mDisplayName
+ " carrierName=" + mCarrierName + " nameSource=" + mNameSource
@@ -639,17 +660,17 @@ public class SubscriptionInfo implements Parcelable {
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
- + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
- + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered
- + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+ + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+ + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
}
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
- mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled,
- mCarrierId);
+ mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+ mIsGroupDisabled, mCarrierId);
}
@Override
@@ -680,6 +701,7 @@ public class SubscriptionInfo implements Parcelable {
&& Objects.equals(mMcc, toCompare.mMcc)
&& Objects.equals(mMnc, toCompare.mMnc)
&& Objects.equals(mCountryIso, toCompare.mCountryIso)
+ && Objects.equals(mCardString, toCompare.mCardString)
&& Objects.equals(mCardId, toCompare.mCardId)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1ea5f46f4ee8..2c712a1c36e1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1198,7 +1198,8 @@ public class SubscriptionManager {
}
/**
- * Request a refresh of the platform cache of profile information.
+ * Request a refresh of the platform cache of profile information for the eUICC which
+ * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
*
* <p>Should be called by the EuiccService implementation whenever this information changes due
* to an operation done outside the scope of a request initiated by the platform to the
@@ -1206,17 +1207,50 @@ public class SubscriptionManager {
* were made through the EuiccService.
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ *
* @hide
*/
@SystemApi
public void requestEmbeddedSubscriptionInfoListRefresh() {
+ int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.requestEmbeddedSubscriptionInfoListRefresh();
+ iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
} catch (RemoteException ex) {
- // ignore it
+ logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+ }
+ }
+
+ /**
+ * Request a refresh of the platform cache of profile information for the eUICC with the given
+ * {@code cardId}.
+ *
+ * <p>Should be called by the EuiccService implementation whenever this information changes due
+ * to an operation done outside the scope of a request initiated by the platform to the
+ * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+ * were made through the EuiccService.
+ *
+ * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @param cardId the card ID of the eUICC.
+ *
+ * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+ }
+ } catch (RemoteException ex) {
+ logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e0632b1e0392..f241d45bf1e2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -625,8 +625,6 @@ public class TelephonyManager {
* The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
* The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
* The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
- * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
- * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
*
* <p class="note">
* Requires the READ_PRECISE_PHONE_STATE permission.
@@ -634,12 +632,10 @@ public class TelephonyManager {
* @see #EXTRA_RINGING_CALL_STATE
* @see #EXTRA_FOREGROUND_CALL_STATE
* @see #EXTRA_BACKGROUND_CALL_STATE
- * @see #EXTRA_DISCONNECT_CAUSE
- * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
*
* <p class="note">
* Requires the READ_PRECISE_PHONE_STATE permission.
- *
+ * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -647,8 +643,28 @@ public class TelephonyManager {
"android.intent.action.PRECISE_CALL_STATE";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current ringing call.
+ * Broadcast intent action indicating that call disconnect cause has changed.
+ *
+ * <p>
+ * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+ * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_DISCONNECT_CAUSE
+ * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED =
+ "android.intent.action.CALL_DISCONNECT_CAUSE";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current ringing call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -670,8 +686,9 @@ public class TelephonyManager {
public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current foreground call.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current foreground call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -693,8 +710,9 @@ public class TelephonyManager {
public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current background call.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current background call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -716,8 +734,9 @@ public class TelephonyManager {
public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the disconnect cause.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the disconnect cause.
*
* @see DisconnectCause
*
@@ -730,8 +749,9 @@ public class TelephonyManager {
public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the disconnect cause provided by the RIL.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the disconnect cause provided by the RIL.
*
* @see PreciseDisconnectCause
*
@@ -4888,7 +4908,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
public void requestCellInfoUpdate(
- @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+ @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
ITelephony telephony = getITelephony();
if (telephony == null) return;
@@ -6362,8 +6382,9 @@ public class TelephonyManager {
public @PrefNetworkMode int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.getPreferredNetworkType(subId);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -6373,6 +6394,37 @@ public class TelephonyManager {
}
/**
+ * Get the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @return a 32-bit bitmap.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public @NetworkTypeBitMask int getPreferredNetworkTypeBitmap() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return RadioAccessFamily.getRafFromNetworkType(
+ telephony.getPreferredNetworkType(getSubId()));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+ }
+ return 0;
+ }
+
+ /**
* Sets the network selection mode to automatic.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6587,6 +6639,37 @@ public class TelephonyManager {
}
/**
+ * Set the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param networkTypeBitmap a 32-bit bitmap.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask int networkTypeBitmap) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setPreferredNetworkType(
+ getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmap));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 78fc0bc487bf..00cf9c3577ec 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -55,5 +55,6 @@ oneway interface IPhoneStateListener {
void onPreferredDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
+ void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 65eedb83c4cc..d169b7d04f5c 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -104,7 +104,7 @@ interface ISub {
/**
* @see android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
*/
- oneway void requestEmbeddedSubscriptionInfoListRefresh();
+ oneway void requestEmbeddedSubscriptionInfoListRefresh(int cardId);
/**
* Add a new SubscriptionInfo to subinfo database if needed
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 553e3fb9d219..0edc0026722b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,8 +284,6 @@ public final class TelephonyPermissions {
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- Log.wtf(LOG_TAG,
- "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
// If the device identifier check is enabled then enforce the new access requirements for
// both 1P and 3P apps.
boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -295,17 +293,40 @@ public final class TelephonyPermissions {
boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
boolean is3PApp = true;
+ // Also check if the application is a preloaded non-privileged app; if so there is a
+ // separate setting to relax the check for these apps to ensure users can relax the check
+ // for 3P or non-priv apps as needed while continuing to test the other.
+ boolean relaxNonPrivDeviceIdentifierCheck = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
+ boolean isNonPrivApp = false;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
- if (callingPackageInfo.isSystemApp()) {
+ if (callingPackageInfo.isPrivilegedApp()) {
is3PApp = false;
+ } else if (callingPackageInfo.isSystemApp()) {
+ is3PApp = false;
+ isNonPrivApp = true;
}
} catch (PackageManager.NameNotFoundException e) {
// If the application info for the calling package could not be found then assume the
// calling app is a 3P app to detect any issues with the check
+ Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
+ e);
}
- if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
+ Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+ // The new Q restrictions for device identifier access will be enforced if any of the
+ // following are true:
+ // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
+ // - The app requesting a device identifier is not a preloaded app (3P), and the
+ // PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
+ // - The app requesting a device identifier is a preloaded app but is not a privileged app,
+ // and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
+ if (enableDeviceIdentifierCheck
+ || (is3PApp && !relax3PDeviceIdentifierCheck)
+ || (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
if (callingPackage != null) {
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index df34c7310b63..ab9bd84b05cb 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -25,7 +25,6 @@ import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -48,7 +47,6 @@ import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
-import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
@@ -74,9 +72,6 @@ import java.util.Set;
public class DhcpServerTest {
private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
private static final String TEST_IFACE = "testiface";
- private static final MacAddress TEST_IFACE_MAC = MacAddress.fromString("11:22:33:44:55:66");
- private static final InterfaceParams TEST_IFACEPARAMS =
- new InterfaceParams(TEST_IFACE, 1, TEST_IFACE_MAC);
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
@@ -149,7 +144,7 @@ public class DhcpServerTest {
.build();
mLooper = new TestLooper();
- mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACEPARAMS, servingParams,
+ mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
mServer.start();
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index cff0b5469d47..2c675c68a076 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -404,7 +404,7 @@ public class IpServerTest {
private void assertDhcpStarted(IpPrefix expectedPrefix) {
verify(mDependencies, times(1)).makeDhcpServer(
- eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
+ eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
verify(mDhcpServer, times(1)).start();
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
// Last address byte is random
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index bca9be772704..e6b43d286a3d 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -240,7 +240,7 @@ public class TetheringTest {
}
@Override
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ public DhcpServer makeDhcpServer(Looper looper, String ifName,
DhcpServingParams params, SharedLog log) {
return mDhcpServer;
}
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 5fa065a9135a..d18c126a96c1 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -21,7 +21,9 @@ import android.app.Fragment;
import android.app.FragmentController;
import android.app.FragmentHostCallback;
import android.app.FragmentManagerNonConfig;
+import android.content.Context;
import android.graphics.PixelFormat;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
@@ -75,7 +77,7 @@ public abstract class BaseFragmentTest {
TestableLooper.get(this).runWithLooper(() -> {
mHandler = new Handler();
- mFragment = mCls.newInstance();
+ mFragment = instantiate(mContext, mCls.getName(), null);
mFragments = FragmentController.createController(new HostCallbacks());
mFragments.attachHost(null);
mFragments.getFragmentManager().beginTransaction()
@@ -187,6 +189,13 @@ public abstract class BaseFragmentTest {
TestableLooper.get(this).processAllMessages();
}
+ /**
+ * Method available for override to replace fragment instantiation.
+ */
+ protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) {
+ return Fragment.instantiate(context, className, arguments);
+ }
+
private View findViewById(int id) {
return mView.findViewById(id);
}
@@ -206,6 +215,11 @@ public abstract class BaseFragmentTest {
}
@Override
+ public Fragment instantiate(Context context, String className, Bundle arguments) {
+ return BaseFragmentTest.this.instantiate(context, className, arguments);
+ }
+
+ @Override
public boolean onShouldSaveFragmentState(Fragment fragment) {
return true; // True for now.
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c91134c167ed..58702dc465cc 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@ struct ParsedResource {
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool allow_new = false;
- Maybe<Overlayable> overlayable;
+ Maybe<OverlayableItem> overlayable_item;
std::string comment;
std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed
}
}
- if (res->overlayable) {
- if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
+ if (res->overlayable_item) {
+ if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
return false;
}
}
@@ -1059,92 +1059,119 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out
bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
diag_->Warn(DiagMessage(out_resource->source)
- << "ignoring configuration '" << out_resource->config
- << "' for <overlayable> tag");
+ << "ignoring configuration '" << out_resource->config
+ << "' for <overlayable> tag");
+ }
+
+ Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!overlayable_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<overlayable> tag must have a 'name' attribute");
+ return false;
}
+ const std::string kActorUriScheme =
+ android::base::StringPrintf("%s://", Overlayable::kActorScheme);
+ Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+ if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "specified <overlayable> tag 'actor' attribute must use the scheme '"
+ << Overlayable::kActorScheme << "'");
+ return false;
+ }
+
+ // Create a overlayable entry grouping that represents this <overlayable>
+ auto overlayable = std::make_shared<Overlayable>(
+ overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
+ out_resource->source);
+
bool error = false;
std::string comment;
- Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
+ OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
- // Break the loop when exiting the overlayable element
+ // Break the loop when exiting the <overlayable>
break;
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
- // Clear the current policies when exiting the policy element
- current_policies = Overlayable::Policy::kNone;
+ // Clear the current policies when exiting the <policy> tags
+ current_policies = OverlayableItem::Policy::kNone;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
- // Get the comment of individual item elements
+ // Retrieve the comment of individual <item> tags
comment = parser->comment();
continue;
} else if (event != xml::XmlPullParser::Event::kStartElement) {
- // Skip to the next element
+ // Skip to the start of the next element
continue;
}
- const Source item_source = source_.WithLine(parser->line_number());
+ const Source element_source = source_.WithLine(parser->line_number());
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
if (element_namespace.empty() && element_name == "item") {
// Items specify the name and type of resource that should be overlayable
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
- if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'name' attribute");
+ Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!item_name) {
+ diag_->Error(DiagMessage(element_source)
+ << "<item> within an <overlayable> must have a 'name' attribute");
error = true;
continue;
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
- if (!maybe_type) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'type' attribute");
+ Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!item_type) {
+ diag_->Error(DiagMessage(element_source)
+ << "<item> within an <overlayable> must have a 'type' attribute");
error = true;
continue;
}
- const ResourceType* type = ParseResourceType(maybe_type.value());
+ const ResourceType* type = ParseResourceType(item_type.value());
if (type == nullptr) {
- diag_->Error(DiagMessage(item_source)
- << "invalid resource type '" << maybe_type.value()
+ diag_->Error(DiagMessage(element_source)
+ << "invalid resource type '" << item_type.value()
<< "' in <item> within an <overlayable>");
error = true;
continue;
}
- ParsedResource child_resource;
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies = current_policies;
+ overlayable_item.comment = comment;
+ overlayable_item.source = element_source;
+
+ ParsedResource child_resource{};
child_resource.name.type = *type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+ child_resource.name.entry = item_name.value().to_string();
+ child_resource.overlayable_item = overlayable_item;
out_resource->child_resources.push_back(std::move(child_resource));
} else if (element_namespace.empty() && element_name == "policy") {
- if (current_policies != Overlayable::Policy::kNone) {
+ if (current_policies != OverlayableItem::Policy::kNone) {
// If the policy list is not empty, then we are currently inside a policy element
- diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
+ diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
- // policies
+ // policies. Items within the policy tag will have the specified policy.
for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
if (trimmed_part == "public") {
- current_policies |= Overlayable::Policy::kPublic;
+ current_policies |= OverlayableItem::Policy::kPublic;
} else if (trimmed_part == "product") {
- current_policies |= Overlayable::Policy::kProduct;
+ current_policies |= OverlayableItem::Policy::kProduct;
} else if (trimmed_part == "product_services") {
- current_policies |= Overlayable::Policy::kProductServices;
+ current_policies |= OverlayableItem::Policy::kProductServices;
} else if (trimmed_part == "system") {
- current_policies |= Overlayable::Policy::kSystem;
+ current_policies |= OverlayableItem::Policy::kSystem;
} else if (trimmed_part == "vendor") {
- current_policies |= Overlayable::Policy::kVendor;
+ current_policies |= OverlayableItem::Policy::kVendor;
} else {
- diag_->Error(DiagMessage(item_source)
+ diag_->Error(DiagMessage(element_source)
<< "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
continue;
@@ -1152,11 +1179,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
}
}
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+ diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
<< " in <overlayable>");
error = true;
break;
}
+
+ comment.clear();
}
return !error;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 03e6197027cb..debca9c1e1ba 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -892,11 +892,8 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
}
TEST_F(ResourceParserTest, ParseOverlayable) {
- std::string input = R"(<overlayable />)";
- EXPECT_TRUE(TestParse(input));
-
- input = R"(
- <overlayable>
+ std::string input = R"(
+ <overlayable name="Name" actor="overlay://theme">
<item type="string" name="foo" />
<item type="drawable" name="bar" />
</overlayable>)";
@@ -905,24 +902,35 @@ TEST_F(ResourceParserTest, ParseOverlayable) {
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
}
-TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
- std::string input = R"(<overlayable />)";
- EXPECT_TRUE(TestParse(input));
+TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
+ EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
+ EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
+ EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
+}
- input = R"(
- <overlayable>
+TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
+ EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
+ std::string input = R"(
+ <overlayable name="Name">
<item type="string" name="foo" />
<policy type="product">
<item type="string" name="bar" />
@@ -945,49 +953,55 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
}
TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="illegal_policy">
<item type="string" name="foo" />
</policy>
@@ -995,7 +1009,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item name="foo" />
</policy>
@@ -1003,7 +1017,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor">
<item type="string" />
</policy>
@@ -1013,7 +1027,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor|product_services">
<item type="string" name="foo" />
</policy>
@@ -1026,39 +1040,59 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
- | Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
+ | OverlayableItem::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kSystem));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
+ <item type="string" name="foo" />
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable name="Name">
+ <item type="string" name="foo" />
+ </overlayable>
+ <overlayable name="Name">
<item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable name="Name">
+ <item type="string" name="foo" />
+ </overlayable>
+ <overlayable name="Other">
<item type="string" name="foo" />
</overlayable>)";
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name" actor="overlay://my.actor.one">
<item type="string" name="foo" />
</overlayable>
- <overlayable>
+ <overlayable name="Other" actor="overlay://my.actor.two">
<item type="string" name="foo" />
</overlayable>)";
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
<item type="string" name="foo" />
@@ -1067,7 +1101,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1076,7 +1110,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1087,13 +1121,13 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
</overlayable>
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1103,7 +1137,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor|product">
<policy type="product_services">
<item type="string" name="foo" />
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 54633ad5c5e3..dbd0a0ca1799 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -40,6 +40,8 @@ using ::android::base::StringPrintf;
namespace aapt {
+const char* Overlayable::kActorScheme = "overlay";
+
static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew&
return true;
}
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics* diag) {
return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}
bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
- const Overlayable& overlayable, IDiagnostics* diag) {
+ const OverlayableItem& overlayable, IDiagnostics* diag) {
return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
+ const OverlayableItem& overlayable,
NameValidator name_validator, IDiagnostics *diag) {
CHECK(diag != nullptr);
@@ -647,14 +650,15 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla
ResourceTableType* type = package->FindOrCreateType(name.type);
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
- if (entry->overlayable) {
+ if (entry->overlayable_item) {
diag->Error(DiagMessage(overlayable.source)
- << "duplicate overlayable declaration for resource '" << name << "'");
- diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+ << "duplicate overlayable declaration for resource '" << name << "'");
+ diag->Error(DiagMessage(entry->overlayable_item.value().source)
+ << "previous declaration here");
return false;
}
- entry->overlayable = overlayable;
+ entry->overlayable_item = overlayable;
return true;
}
@@ -690,7 +694,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
new_entry->id = entry->id;
new_entry->visibility = entry->visibility;
new_entry->allow_new = entry->allow_new;
- new_entry->overlayable = entry->overlayable;
+ new_entry->overlayable_item = entry->overlayable_item;
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e646f5be43c7..eaf6a47a15fd 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,10 +57,27 @@ struct AllowNew {
std::string comment;
};
-// Represents a declaration that a resource is overlayable at runtime.
struct Overlayable {
+ Overlayable() = default;
+ Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
+ : name(name.to_string()), actor(actor.to_string()) {}
+ Overlayable(const android::StringPiece& name, const android::StringPiece& actor,
+ const Source& source)
+ : name(name.to_string()), actor(actor.to_string()), source(source ){}
+
+ static const char* kActorScheme;
+ std::string name;
+ std::string actor;
+ Source source;
+};
+
+// Represents a declaration that a resource is overlayable at runtime.
+struct OverlayableItem {
+ explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
+ : overlayable(overlayable) {}
// Represents the types overlays that are allowed to overlay the resource.
+ typedef uint32_t PolicyFlags;
enum Policy : uint32_t {
kNone = 0x00,
@@ -80,11 +97,10 @@ struct Overlayable {
kProductServices = 0x10
};
- typedef uint32_t PolicyFlags;
+ std::shared_ptr<Overlayable> overlayable;
PolicyFlags policies = Policy::kNone;
-
- Source source;
std::string comment;
+ Source source;
};
class ResourceConfigValue {
@@ -121,7 +137,7 @@ class ResourceEntry {
Maybe<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- Maybe<Overlayable> overlayable;
+ Maybe<OverlayableItem> overlayable_item;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -251,9 +267,9 @@ class ResourceTable {
bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
const ResourceId& res_id, IDiagnostics* diag);
- bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics *diag);
- bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics* diag);
bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -328,7 +344,7 @@ class ResourceTable {
bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
NameValidator name_validator, IDiagnostics* diag);
- bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+ bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
NameValidator name_validator, IDiagnostics *diag);
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 31095c4d88c8..a733134f123c 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -244,48 +244,90 @@ TEST(ResourceTableTest, SetAllowNew) {
TEST(ResourceTableTest, SetOverlayable) {
ResourceTable table;
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kProductServices;
- overlayable.comment = "comment";
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
+ Source("res/values/overlayable.xml", 40));
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+ overlayable_item.comment = "comment";
+ overlayable_item.source = Source("res/values/overlayable.xml", 42);
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kProductServices));
+ ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
+ EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.source.line, 42);
+}
+
+TEST(ResourceTableTest, SetMultipleOverlayableResources) {
+ ResourceTable table;
+
+ const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+ auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable(group);
+ overlayable.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+
+ const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+ OverlayableItem overlayable2(group);
+ overlayable2.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+
+ const ResourceName baz = test::ParseNameOrDie("android:string/baz");
+ OverlayableItem overlayable3(group);
+ overlayable3.policies = OverlayableItem::Policy::kVendor;
+ ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
+ ResourceTable table;
+
+ const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
+ overlayable_item.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kProductServices));
+ const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+ OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
+ overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
+TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable{};
- overlayable.policies = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable_item(overlayable);
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
- Overlayable overlayable2{};
- overlayable2.policies = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+ OverlayableItem overlayable_item2(overlayable);
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
+TEST(ResourceTableTest, SetOverlayableSameResourcesDifferentNameFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable{};
- overlayable.policies = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable_item(overlayable);
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
- Overlayable overlayable2{};
- overlayable2.policies = Overlayable::Policy::kVendor;
- ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+ auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
+ OverlayableItem overlayable_item2(overlayable2);
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
}
TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 81a2c2e5cc02..da541be9502b 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -49,6 +49,9 @@ message ResourceTable {
// Resource definitions corresponding to an Android package.
repeated Package package = 2;
+
+ // The <overlayable> declarations within the resource table.
+ repeated Overlayable overlayable = 3;
}
// A package ID in the range [0x00, 0xff].
@@ -133,8 +136,20 @@ message AllowNew {
string comment = 2;
}
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a set of overlayable resources.
message Overlayable {
+ // The name of the <overlyabale>.
+ string name = 1;
+
+ // The location of the <overlyabale> declaration in the source.
+ Source source = 2;
+
+ // The component responsible for enabling and disabling overlays targeting this <overlayable>.
+ string actor = 3;
+}
+
+// Represents an overlayable <item> declaration within an <overlayable> tag.
+message OverlayableItem {
enum Policy {
PUBLIC = 0;
SYSTEM = 1;
@@ -143,14 +158,18 @@ message Overlayable {
PRODUCT_SERVICES = 4;
}
- // Where this declaration was defined in source.
+ // The location of the <item> declaration in source.
Source source = 1;
// Any comment associated with the declaration.
string comment = 2;
- // The policy defined in the overlayable declaration.
+ // The policy defined by the enclosing <policy> tag of this <item>.
repeated Policy policy = 3;
+
+ // The index into overlayable list that points to the <overlayable> tag that contains
+ // this <item>.
+ uint32 overlayable_idx = 4;
}
// An entry ID in the range [0x0000, 0xffff].
@@ -180,7 +199,7 @@ message Entry {
AllowNew allow_new = 4;
// Whether this resource can be overlaid by a runtime resource overlay (RRO).
- Overlayable overlayable = 5;
+ OverlayableItem overlayable_item = 5;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 11a4074cd3cd..e17fb4783a45 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -43,8 +43,10 @@ enum {
PERMISSION_ATTR = 0x01010006,
EXPORTED_ATTR = 0x01010010,
GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+ PRIORITY_ATTR = 0x0101001c,
RESOURCE_ATTR = 0x01010025,
DEBUGGABLE_ATTR = 0x0101000f,
+ TARGET_PACKAGE_ATTR = 0x01010021,
VALUE_ATTR = 0x01010024,
VERSION_CODE_ATTR = 0x0101021b,
VERSION_NAME_ATTR = 0x0101021c,
@@ -77,8 +79,11 @@ enum {
ISGAME_ATTR = 0x10103f4,
VERSION_ATTR = 0x01010519,
CERT_DIGEST_ATTR = 0x01010548,
- REQUIRED_FEATURE_ATTR = 0x1010557,
- REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+ REQUIRED_FEATURE_ATTR = 0x01010557,
+ REQUIRED_NOT_FEATURE_ATTR = 0x01010558,
+ IS_STATIC_ATTR = 0x0101055a,
+ REQUIRED_SYSTEM_PROPERTY_NAME_ATTR = 0x01010565,
+ REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR = 0x01010566,
COMPILE_SDK_VERSION_ATTR = 0x01010572,
COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
VERSION_MAJOR_ATTR = 0x01010577,
@@ -1586,6 +1591,44 @@ class OriginalPackage : public ManifestExtractor::Element {
}
};
+
+/** Represents <overlay> elements. **/
+class Overlay : public ManifestExtractor::Element {
+ public:
+ Overlay() = default;
+ const std::string* target_package = nullptr;
+ int priority;
+ bool is_static;
+ const std::string* required_property_name = nullptr;
+ const std::string* required_property_value = nullptr;
+
+ void Extract(xml::Element* element) override {
+ target_package = GetAttributeString(FindAttribute(element, TARGET_PACKAGE_ATTR));
+ priority = GetAttributeIntegerDefault(FindAttribute(element, PRIORITY_ATTR), 0);
+ is_static = GetAttributeIntegerDefault(FindAttribute(element, IS_STATIC_ATTR), false) != 0;
+ required_property_name = GetAttributeString(
+ FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_NAME_ATTR));
+ required_property_value = GetAttributeString(
+ FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR));
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(StringPrintf("overlay:"));
+ if (target_package) {
+ printer->Print(StringPrintf(" targetPackage='%s'", target_package->c_str()));
+ }
+ printer->Print(StringPrintf(" priority='%d'", priority));
+ printer->Print(StringPrintf(" isStatic='%s'", is_static ? "true" : "false"));
+ if (required_property_name) {
+ printer->Print(StringPrintf(" requiredPropertyName='%s'", required_property_name->c_str()));
+ }
+ if (required_property_value) {
+ printer->Print(StringPrintf(" requiredPropertyValue='%s'", required_property_value->c_str()));
+ }
+ printer->Print("\n");
+ }
+};
+
/** * Represents <package-verifier> elements. **/
class PackageVerifier : public ManifestExtractor::Element {
public:
@@ -2166,6 +2209,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"meta-data", std::is_base_of<MetaData, T>::value},
{"manifest", std::is_base_of<Manifest, T>::value},
{"original-package", std::is_base_of<OriginalPackage, T>::value},
+ {"overlay", std::is_base_of<Overlay, T>::value},
{"package-verifier", std::is_base_of<PackageVerifier, T>::value},
{"permission", std::is_base_of<Permission, T>::value},
{"provider", std::is_base_of<Provider, T>::value},
@@ -2215,6 +2259,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"manifest", &CreateType<Manifest>},
{"meta-data", &CreateType<MetaData>},
{"original-package", &CreateType<OriginalPackage>},
+ {"overlay", &CreateType<Overlay>},
{"package-verifier", &CreateType<PackageVerifier>},
{"permission", &CreateType<Permission>},
{"provider", &CreateType<Provider>},
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 61ebd4ee26ca..c496ff0e159b 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -434,6 +434,8 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
return false;
}
+ auto overlayable = std::make_shared<Overlayable>();
+
ResChunkPullParser parser(GetChunkData(chunk),
GetChunkDataLen(chunk));
while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
@@ -441,25 +443,25 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+ OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies |= Overlayable::Policy::kPublic;
+ policies |= OverlayableItem::Policy::kPublic;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies |= Overlayable::Policy::kSystem;
+ policies |= OverlayableItem::Policy::kSystem;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies |= Overlayable::Policy::kVendor;
+ policies |= OverlayableItem::Policy::kVendor;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies |= Overlayable::Policy::kProduct;
+ policies |= OverlayableItem::Policy::kProduct;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
- policies |= Overlayable::Policy::kProductServices;
+ policies |= OverlayableItem::Policy::kProductServices;
}
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,10 +480,10 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
return false;
}
- Overlayable overlayable{};
- overlayable.source = source_.WithLine(0);
- overlayable.policies = policies;
- if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.source = source_.WithLine(0);
+ overlayable_item.policies = policies;
+ if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
return false;
}
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 200e2d468500..931d57b1c08a 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,29 +429,29 @@ class PackageFlattener {
CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
for (auto& entry : type->entries) {
CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
- if (!entry->overlayable) {
+ if (!entry->overlayable_item) {
continue;
}
- Overlayable overlayable = entry->overlayable.value();
- uint32_t policy_flags = Overlayable::Policy::kNone;
- if (overlayable.policies & Overlayable::Policy::kPublic) {
+ OverlayableItem& overlayable = entry->overlayable_item.value();
+ uint32_t policy_flags = OverlayableItem::Policy::kNone;
+ if (overlayable.policies & OverlayableItem::Policy::kPublic) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
- if (overlayable.policies & Overlayable::Policy::kSystem) {
+ if (overlayable.policies & OverlayableItem::Policy::kSystem) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kVendor) {
+ if (overlayable.policies & OverlayableItem::Policy::kVendor) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kProduct) {
+ if (overlayable.policies & OverlayableItem::Policy::kProduct) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
}
- if (overlayable.policies == Overlayable::Policy::kNone) {
+ if (overlayable.policies == OverlayableItem::Policy::kNone) {
// Encode overlayable entries defined without a policy as publicly overlayable
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e99ab1f37761..a5fb6fd6d7aa 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,17 +628,17 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kSystem;
- overlayable.policies |= Overlayable::Policy::kVendor;
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item.policies |= OverlayableItem::Policy::kVendor;
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name, ResourceId(0x7f020000))
- .SetOverlayable(name, overlayable)
+ .SetOverlayable(name, overlayable_item)
.Build();
ResourceTable output_table;
@@ -647,45 +647,46 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) {
auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kVendor
- | Overlayable::Policy::kProduct);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kVendor
+ | OverlayableItem::Policy::kProduct);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
- std::string name_zero = "com.app.test:integer/overlayable_zero";
- Overlayable overlayable_zero{};
- overlayable_zero.policies |= Overlayable::Policy::kProduct;
- overlayable_zero.policies |= Overlayable::Policy::kSystem;
- overlayable_zero.policies |= Overlayable::Policy::kProductServices;
-
- std::string name_one = "com.app.test:integer/overlayable_one";
- Overlayable overlayable_one{};
- overlayable_one.policies |= Overlayable::Policy::kPublic;
- overlayable_one.policies |= Overlayable::Policy::kProductServices;
-
- std::string name_two = "com.app.test:integer/overlayable_two";
- Overlayable overlayable_two{};
- overlayable_two.policies |= Overlayable::Policy::kProduct;
- overlayable_two.policies |= Overlayable::Policy::kSystem;
- overlayable_two.policies |= Overlayable::Policy::kVendor;
-
- std::string name_three = "com.app.test:integer/overlayable_three";
- Overlayable overlayable_three{};
+ auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
+ std::string name_zero = "com.app.test:integer/overlayable_zero_item";
+ OverlayableItem overlayable_zero_item(overlayable);
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
+
+ std::string name_one = "com.app.test:integer/overlayable_one_item";
+ OverlayableItem overlayable_one_item(overlayable);
+ overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
+
+ std::string name_two = "com.app.test:integer/overlayable_two_item";
+ OverlayableItem overlayable_two_item(overlayable);
+ overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
+
+ std::string name_three = "com.app.test:integer/overlayable_three_item";
+ OverlayableItem overlayable_three_item(overlayable);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
- .SetOverlayable(name_zero, overlayable_zero)
+ .SetOverlayable(name_zero, overlayable_zero_item)
.AddSimple(name_one, ResourceId(0x7f020001))
- .SetOverlayable(name_one, overlayable_one)
+ .SetOverlayable(name_one, overlayable_one_item)
.AddSimple(name_two, ResourceId(0x7f020002))
- .SetOverlayable(name_two, overlayable_two)
+ .SetOverlayable(name_two, overlayable_two_item)
.AddSimple(name_three, ResourceId(0x7f020003))
- .SetOverlayable(name_three, overlayable_three)
+ .SetOverlayable(name_three, overlayable_three_item)
.Build();
ResourceTable output_table;
@@ -694,35 +695,35 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct
- | Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
- | Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
+ | OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct
- | Overlayable::Policy::kVendor);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kVendor);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
}
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index cf2ab0f45ad6..6b5746d63bf8 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -373,9 +373,44 @@ static Visibility::Level DeserializeVisibilityFromPb(const pb::Visibility::Level
return Visibility::Level::kUndefined;
}
+bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
+ const android::ResStringPool& src_pool,
+ OverlayableItem* out_overlayable, std::string* out_error) {
+ for (const int policy : pb_overlayable.policy()) {
+ switch (policy) {
+ case pb::OverlayableItem::PUBLIC:
+ out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+ break;
+ case pb::OverlayableItem::SYSTEM:
+ out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+ break;
+ case pb::OverlayableItem::VENDOR:
+ out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+ break;
+ case pb::OverlayableItem::PRODUCT:
+ out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+ break;
+ case pb::OverlayableItem::PRODUCT_SERVICES:
+ out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
+ break;
+ default:
+ *out_error = "unknown overlayable policy";
+ return false;
+ }
+ }
+
+ if (pb_overlayable.has_source()) {
+ DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source);
+ }
+
+ out_overlayable->comment = pb_overlayable.comment();
+ return true;
+}
+
static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
- io::IFileCollection* files, ResourceTable* out_table,
- std::string* out_error) {
+ io::IFileCollection* files,
+ const std::vector<std::shared_ptr<Overlayable>>& overlayables,
+ ResourceTable* out_table, std::string* out_error) {
Maybe<uint8_t> id;
if (pb_package.has_package_id()) {
id = static_cast<uint8_t>(pb_package.package_id().id());
@@ -437,39 +472,22 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
entry->allow_new = std::move(allow_new);
}
- if (pb_entry.has_overlayable()) {
- Overlayable overlayable{};
-
- const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
- for (const int policy : pb_overlayable.policy()) {
- switch (policy) {
- case pb::Overlayable::PUBLIC:
- overlayable.policies |= Overlayable::Policy::kPublic;
- break;
- case pb::Overlayable::SYSTEM:
- overlayable.policies |= Overlayable::Policy::kSystem;
- break;
- case pb::Overlayable::VENDOR:
- overlayable.policies |= Overlayable::Policy::kVendor;
- break;
- case pb::Overlayable::PRODUCT:
- overlayable.policies |= Overlayable::Policy::kProduct;
- break;
- case pb::Overlayable::PRODUCT_SERVICES:
- overlayable.policies |= Overlayable::Policy::kProductServices;
- break;
- default:
- *out_error = "unknown overlayable policy";
- return false;
- }
+ if (pb_entry.has_overlayable_item()) {
+ // Find the overlayable to which this item belongs
+ pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
+ if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
+ *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
+ pb_overlayable_item.overlayable_idx());
+ return false;
}
- if (pb_overlayable.has_source()) {
- DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+ OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]);
+ if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item,
+ out_error)) {
+ return false;
}
- overlayable.comment = pb_overlayable.comment();
- entry->overlayable = overlayable;
+ entry->overlayable_item = std::move(overlayable_item);
}
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
@@ -522,8 +540,19 @@ bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollecti
}
}
+ // Deserialize the overlayable groups of the table
+ std::vector<std::shared_ptr<Overlayable>> overlayables;
+ for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
+ auto group = std::make_shared<Overlayable>(pb_overlayable.name(), pb_overlayable.actor());
+ if (pb_overlayable.has_source()) {
+ DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source);
+ }
+ overlayables.push_back(group);
+ }
+
for (const pb::Package& pb_package : pb_table.package()) {
- if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
+ if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table,
+ out_error)) {
return false;
}
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 70bf8684f8a8..76fbb464b62a 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -272,9 +272,57 @@ void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_
out_pb_config->set_sdk_version(config.sdkVersion);
}
+static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
+ std::vector<Overlayable*>& serialized_overlayables,
+ StringPool* source_pool, pb::Entry* pb_entry,
+ pb::ResourceTable* pb_table) {
+ // Retrieve the index of the overlayable in the list of groups that have already been serialized.
+ size_t i;
+ for (i = 0 ; i < serialized_overlayables.size(); i++) {
+ if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
+ break;
+ }
+ }
+
+ // Serialize the overlayable if it has not been serialized already.
+ if (i == serialized_overlayables.size()) {
+ serialized_overlayables.push_back(overlayable_item.overlayable.get());
+ pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
+ pb_overlayable->set_name(overlayable_item.overlayable->name);
+ pb_overlayable->set_actor(overlayable_item.overlayable->actor);
+ SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+ pb_overlayable->mutable_source());
+ }
+
+ pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
+ pb_overlayable_item->set_overlayable_idx(i);
+
+ if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
+ }
+
+ SerializeSourceToPb(overlayable_item.source, source_pool,
+ pb_overlayable_item->mutable_source());
+ pb_overlayable_item->set_comment(overlayable_item.comment);
+}
+
void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
IDiagnostics* diag) {
StringPool source_pool;
+
+ std::vector<Overlayable*> overlayables;
for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
pb::Package* pb_package = out_table->add_package();
if (package->id) {
@@ -310,29 +358,9 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
- if (entry->overlayable) {
- pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-
- Overlayable overlayable = entry->overlayable.value();
- if (overlayable.policies & Overlayable::Policy::kPublic) {
- pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
- }
- if (overlayable.policies & Overlayable::Policy::kProduct) {
- pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
- }
- if (overlayable.policies & Overlayable::Policy::kProductServices) {
- pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
- }
- if (overlayable.policies & Overlayable::Policy::kSystem) {
- pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
- }
- if (overlayable.policies & Overlayable::Policy::kVendor) {
- pb_overlayable->add_policy(pb::Overlayable::VENDOR);
- }
-
- SerializeSourceToPb(overlayable.source, &source_pool,
- pb_overlayable->mutable_source());
- pb_overlayable->set_comment(overlayable.comment);
+ if (entry->overlayable_item) {
+ SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
+ pb_entry, out_table);
}
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fb913f409f52..4a3c1b86236e 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,8 +93,11 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
// Make an overlayable resource.
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>(
+ "OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
+ overlayable_item.source = Source("res/values/overlayable.xml", 42);
ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
- Overlayable{}, test::GetDiagnostics()));
+ overlayable_item, test::GetDiagnostics()));
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -160,9 +163,15 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("OverlayableName"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
}
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -503,26 +512,31 @@ TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) {
}
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
- Overlayable overlayable_foo{};
- overlayable_foo.policies |= Overlayable::Policy::kSystem;
- overlayable_foo.policies |= Overlayable::Policy::kProduct;
+ OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
+ "CustomizableResources", "overlay://customization"));
+ overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
- Overlayable overlayable_bar{};
- overlayable_bar.policies |= Overlayable::Policy::kProductServices;
- overlayable_bar.policies |= Overlayable::Policy::kVendor;
+ OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
+ "TaskBar", "overlay://theme"));
+ overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+ overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
- Overlayable overlayable_baz{};
- overlayable_baz.policies |= Overlayable::Policy::kPublic;
+ OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
+ "FontPack", "overlay://theme"));
+ overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
- Overlayable overlayable_biz{};
+ OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
+ "Other", "overlay://customization"));
+ overlayable_item_biz.comment ="comment";
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
- .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
- .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
- .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
+ .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
+ .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
+ .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+ .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -538,33 +552,41 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
Maybe<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
- | Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+ | OverlayableItem::Policy::kVendor));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(overlayable_item.comment, Eq("comment"));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
ASSERT_TRUE(search_result);
- ASSERT_FALSE(search_result.value().entry->overlayable);
+ ASSERT_FALSE(search_result.value().entry->overlayable_item);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8cbc03738677..c2340ba65e38 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
}
// Ensure that definitions for values declared as overlayable exist
- if (entry->overlayable && entry->values.empty()) {
- context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+ if (entry->overlayable_item && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
<< "no definition for overlayable symbol '"
<< name << "'");
error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22e1723591a8..cc9fed554350 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,18 +134,18 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
dst_entry->allow_new = std::move(src_entry->allow_new);
}
- if (src_entry->overlayable) {
- if (dst_entry->overlayable) {
+ if (src_entry->overlayable_item) {
+ if (dst_entry->overlayable_item) {
// Do not allow a resource with an overlayable declaration to have that overlayable
// declaration redefined
- context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source)
<< "duplicate overlayable declaration for resource '"
<< src_entry->name << "'");
- context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source)
<< "previous declaration here");
return false;
} else {
- dst_entry->overlayable = std::move(src_entry->overlayable);
+ dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
}
}
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 17b2a83bad04..921d634e583e 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -437,14 +437,16 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
}
TEST_F(TableMergerTest, SetOverlayable) {
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kVendor;
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kVendor;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable)
+ .SetOverlayable("bool/foo", overlayable_item)
.Build();
std::unique_ptr<ResourceTable> table_b =
@@ -463,26 +465,30 @@ TEST_F(TableMergerTest, SetOverlayable) {
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kVendor));
}
TEST_F(TableMergerTest, SetOverlayableLater) {
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
.AddSimple("bool/foo")
.Build();
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kPublic;
- overlayable.policies |= Overlayable::Policy::kProductServices;
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable)
+ .SetOverlayable("bool/foo", overlayable_item)
.Build();
ResourceTable final_table;
@@ -495,27 +501,33 @@ TEST_F(TableMergerTest, SetOverlayableLater) {
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
- | Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
+ | OverlayableItem::Policy::kProductServices));
}
-TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
- Overlayable overlayable_first{};
- overlayable_first.policies |= Overlayable::Policy::kProduct;
+TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
+ auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+ OverlayableItem overlayable_item_first(overlayable_first);
+ overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_first)
+ .SetOverlayable("bool/foo", overlayable_item_first)
.Build();
- Overlayable overlayable_second{};
- overlayable_second.policies |= Overlayable::Policy::kProduct;
+ auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
+ "overlay://theme");
+ OverlayableItem overlayable_item_second(overlayable_second);
+ overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_second)
+ .SetOverlayable("bool/foo", overlayable_item_second)
.Build();
ResourceTable final_table;
@@ -526,21 +538,24 @@ TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
-TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
- Overlayable overlayable_first{};
- overlayable_first.policies |= Overlayable::Policy::kVendor;
+TEST_F(TableMergerTest, SameResourceSameNameFail) {
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+
+ OverlayableItem overlayable_item_first(overlayable);
+ overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo",overlayable_first)
+ .SetOverlayable("bool/foo", overlayable_item_first)
.Build();
- Overlayable overlayable_second{};
- overlayable_second.policies |= Overlayable::Policy::kProduct;
+ OverlayableItem overlayable_item_second(overlayable);
+ overlayable_item_second.policies |= OverlayableItem::Policy::kSystem;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_second)
+ .SetOverlayable("bool/foo", overlayable_item_second)
.Build();
ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9c5b5d36b798..24cd5ba302ea 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
if (!split_entry->id) {
split_entry->id = entry->id;
split_entry->visibility = entry->visibility;
- split_entry->overlayable = entry->overlayable;
+ split_entry->overlayable_item = entry->overlayable_item;
}
// Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 884ec38290f8..9a93f2a7476c 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -136,7 +136,7 @@ ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& na
}
ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
- const Overlayable& overlayable) {
+ const OverlayableItem& overlayable) {
ResourceName res_name = ParseNameOrDie(name);
CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index a12048436e38..c971a1b47fd5 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -74,7 +74,7 @@ class ResourceTableBuilder {
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
Visibility::Level level, bool allow_new = false);
ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
- const Overlayable& overlayable);
+ const OverlayableItem& overlayable);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e6e3cf..6476abd8268e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -209,24 +209,42 @@ class Package():
return self.raw
-def _parse_stream(f, clazz_cb=None, base_f=None):
+def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
+ in_classes_with_base=[]):
api = {}
+ in_classes_with_base = _retry_iterator(in_classes_with_base)
if base_f:
- base_classes = _parse_stream_to_generator(base_f)
+ base_classes = _retry_iterator(_parse_stream_to_generator(base_f))
else:
base_classes = []
- for clazz in _parse_stream_to_generator(f):
- base_class = _parse_to_matching_class(base_classes, clazz)
- if base_class:
- clazz.merge_from(base_class)
-
+ def handle_class(clazz):
if clazz_cb:
clazz_cb(clazz)
else: # In callback mode, don't keep track of the full API
api[clazz.fullname] = clazz
+ def handle_missed_classes_with_base(clazz):
+ for c in _yield_until_matching_class(in_classes_with_base, clazz):
+ base_class = _skip_to_matching_class(base_classes, c)
+ if base_class:
+ handle_class(base_class)
+
+ for clazz in _parse_stream_to_generator(f):
+ # Before looking at clazz, let's see if there's some classes that were not present, but
+ # may have an entry in the base stream.
+ handle_missed_classes_with_base(clazz)
+
+ base_class = _skip_to_matching_class(base_classes, clazz)
+ if base_class:
+ clazz.merge_from(base_class)
+ if out_classes_with_base is not None:
+ out_classes_with_base.append(clazz)
+ handle_class(clazz)
+
+ handle_missed_classes_with_base(None)
+
return api
def _parse_stream_to_generator(f):
@@ -257,18 +275,22 @@ def _parse_stream_to_generator(f):
elif raw.startswith(" field"):
clazz.fields.append(Field(clazz, line, raw, blame))
elif raw.startswith(" }") and clazz:
- while True:
- retry = yield clazz
- if not retry:
- break
- # send() was called, asking us to redeliver clazz on next(). Still need to yield
- # a dummy value to the send() first though.
- if (yield "Returning clazz on next()"):
- raise TypeError("send() must be followed by next(), not send()")
-
-
-def _parse_to_matching_class(classes, needle):
- """Takes a classes generator and parses it until it returns the class we're looking for
+ yield clazz
+
+def _retry_iterator(it):
+ """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+ for e in it:
+ while True:
+ retry = yield e
+ if not retry:
+ break
+ # send() was called, asking us to redeliver clazz on next(). Still need to yield
+ # a dummy value to the send() first though.
+ if (yield "Returning clazz on next()"):
+ raise TypeError("send() must be followed by next(), not send()")
+
+def _skip_to_matching_class(classes, needle):
+ """Takes a classes iterator and consumes entries until it returns the class we're looking for
This relies on classes being sorted by package and class name."""
@@ -276,8 +298,8 @@ def _parse_to_matching_class(classes, needle):
if clazz.pkg.name < needle.pkg.name:
# We haven't reached the right package yet
continue
- if clazz.name < needle.name:
- # We haven't reached the right class yet
+ if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
continue
if clazz.fullname == needle.fullname:
return clazz
@@ -285,6 +307,28 @@ def _parse_to_matching_class(classes, needle):
classes.send(clazz)
return None
+def _yield_until_matching_class(classes, needle):
+ """Takes a class iterator and yields entries it until it reaches the class we're looking for.
+
+ This relies on classes being sorted by package and class name."""
+
+ for clazz in classes:
+ if needle is None:
+ yield clazz
+ elif clazz.pkg.name < needle.pkg.name:
+ # We haven't reached the right package yet
+ yield clazz
+ elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
+ yield clazz
+ elif clazz.fullname == needle.fullname:
+ # Class found, abort.
+ return
+ else:
+ # We ran past the right class. Send it back into the iterator, then abort.
+ classes.send(clazz)
+ return
+
class Failure():
def __init__(self, sig, clazz, detail, error, rule, msg):
self.sig = sig
@@ -1543,12 +1587,14 @@ def examine_clazz(clazz):
verify_singleton(clazz)
-def examine_stream(stream, base_stream=None):
+def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
"""Find all style issues in the given API stream."""
global failures, noticed
failures = {}
noticed = {}
- _parse_stream(stream, examine_clazz, base_f=base_stream)
+ _parse_stream(stream, examine_clazz, base_f=base_stream,
+ in_classes_with_base=in_classes_with_base,
+ out_classes_with_base=out_classes_with_base)
return (failures, noticed)
@@ -1734,19 +1780,24 @@ if __name__ == "__main__":
show_stats(cur, prev)
sys.exit()
+ classes_with_base = []
+
with current_file as f:
if base_current_file:
with base_current_file as base_f:
- cur_fail, cur_noticed = examine_stream(f, base_f)
+ cur_fail, cur_noticed = examine_stream(f, base_f,
+ out_classes_with_base=classes_with_base)
else:
- cur_fail, cur_noticed = examine_stream(f)
+ cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base)
+
if not previous_file is None:
with previous_file as f:
if base_previous_file:
with base_previous_file as base_f:
- prev_fail, prev_noticed = examine_stream(f, base_f)
+ prev_fail, prev_noticed = examine_stream(f, base_f,
+ in_classes_with_base=classes_with_base)
else:
- prev_fail, prev_noticed = examine_stream(f)
+ prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base)
# ignore errors from previous API level
for p in prev_fail:
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 000000000000..8538a3d904f5
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+ python tools/apilint/apilint.py \
+ --base-current <(git show $1:api/current.txt) \
+ --base-previous <(git show $1^:api/current.txt) \
+ <(git show $1:api/system-current.txt) \
+ <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 000000000000..ece69a99f579
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+ return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+ "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+ def test_retry_iterator(self):
+ it = apilint._retry_iterator([1, 2, 3, 4])
+ self.assertEqual(it.next(), 1)
+ self.assertEqual(it.next(), 2)
+ self.assertEqual(it.next(), 3)
+ it.send("retry")
+ self.assertEqual(it.next(), 3)
+ self.assertEqual(it.next(), 4)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_skip_to_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, c3),
+ c3)
+ self.assertEqual(it.next(), c4)
+
+ def test_skip_to_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")),
+ None)
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, c3)),
+ [c1, c2])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))),
+ [c1, c2, c3])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_None(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, None)),
+ [c1, c2, c3, c4])
+
+
+faulty_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+ok_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+system_current_txt = """
+package android.app {
+ public final class WallpaperColors implements android.os.Parcelable {
+ method public int getSomething();
+ }
+}
+""".split('\n')
+
+
+
+class BaseFileTests(unittest.TestCase):
+ def test_base_file_avoids_errors(self):
+ failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt)
+ self.assertEquals(failures, {})
+
+ def test_class_with_base_finds_same_errors(self):
+ failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt,
+ in_classes_with_base=[cls("android.app", "WallpaperColors")])
+ failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt)
+
+ self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys())
+
+ def test_classes_with_base_is_emited(self):
+ classes_with_base = []
+ _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt,
+ out_classes_with_base=classes_with_base)
+ self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
+
+if __name__ == "__main__":
+ unittest.main() \ No newline at end of file
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index faa354788c4e..f6c9c0e86fe4 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -453,7 +453,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
map<string, bool> variableNames;
set<string> parents;
vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
- bool skip[fieldsInOrder.size()];
+ vector<bool> skip(fieldsInOrder.size());
const Destination incidentDest = getPrivacyFlags(descriptor).dest();
for (size_t i=0; i<fieldsInOrder.size(); i++) {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a8e82b3d1bc6..0362a1b491ea 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -197,5 +197,7 @@ interface IWifiManager
int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
String[] getFactoryMacAddresses();
+
+ void setDeviceMobilityState(int state);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8cb20638dd42..a7c2ff0f875c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4476,4 +4476,69 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = {
+ DEVICE_MOBILITY_STATE_UNKNOWN,
+ DEVICE_MOBILITY_STATE_HIGH_MVMT,
+ DEVICE_MOBILITY_STATE_LOW_MVMT,
+ DEVICE_MOBILITY_STATE_STATIONARY})
+ public @interface DeviceMobilityState {}
+
+ /**
+ * Unknown device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0;
+
+ /**
+ * High movement device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1;
+
+ /**
+ * Low movement device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2;
+
+ /**
+ * Stationary device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3;
+
+ /**
+ * Updates the device mobility state. Wifi uses this information to adjust the interval between
+ * Wifi scans in order to balance power consumption with scan accuracy.
+ * @param state the updated device mobility state
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
+ public void setDeviceMobilityState(@DeviceMobilityState int state) {
+ try {
+ mService.setDeviceMobilityState(state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 73341ac55b67..401b652289cd 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -45,7 +45,7 @@ LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
core-test-rules \
guava \
mockito-target-minus-junit4 \
diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml
index 4eaca2b98a1d..b6c38bcf5a72 100644
--- a/wifi/tests/AndroidManifest.xml
+++ b/wifi/tests/AndroidManifest.xml
@@ -30,7 +30,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.net.wifi.test"
android:label="Frameworks Wifi API Tests">
</instrumentation>
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 45c7a1708d20..cae19e46c6af 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="FrameworksWifiApiTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.wifi.test" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/wifi/tests/README.md b/wifi/tests/README.md
index b418abd5e46b..b0594f2d29b1 100644
--- a/wifi/tests/README.md
+++ b/wifi/tests/README.md
@@ -37,7 +37,7 @@ runtests.sh -e class android.net.wifi.WifiScannerTest
If you manually build and push the test APK to the device you can run tests using
```
-adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
```
## Adding Tests
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
index 4e52b8f23399..2caf8a59b9fb 100755
--- a/wifi/tests/runtests.sh
+++ b/wifi/tests/runtests.sh
@@ -22,4 +22,4 @@ adb wait-for-device
adb install -r -g "$OUT/data/app/FrameworksWifiApiTests/FrameworksWifiApiTests.apk"
adb shell am instrument --no-hidden-api-checks -w "$@" \
- 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+ 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
index 0c3bf3b9ef2e..917b1df74da7 100644
--- a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
+++ b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
@@ -22,7 +22,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 458c43dcd692..54ec32502878 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -22,15 +22,14 @@ import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.validateMockitoUsage;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
+
+import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8d97307df9ca..c744f18cf408 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -26,7 +26,8 @@ import android.net.MacAddress;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index e569efef5d91..6ec64ffb0a06 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -19,8 +19,8 @@ package android.net.wifi;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -28,7 +28,8 @@ import android.net.wifi.WifiEnterpriseConfig.Eap;
import android.net.wifi.WifiEnterpriseConfig.Phase2;
import android.os.Parcel;
import android.security.Credentials;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -36,7 +37,6 @@ import org.junit.Test;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
-
/**
* Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index f9fb0626adaf..fb0af5fbcce3 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -21,7 +21,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 1001b100cb3b..c43948bb0c04 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -66,7 +66,8 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index f8ab8a2cd5d7..2258e4dd79e5 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -25,9 +25,10 @@ import android.net.MatchAllNetworkSpecifier;
import android.net.NetworkRequest;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 2505499c85d7..83627ad524d2 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -29,7 +29,8 @@ import android.net.MacAddress;
import android.net.NetworkSpecifier;
import android.os.PatternMatcher;
import android.os.Process;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 997282b24b46..fdd11a385651 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -29,8 +29,9 @@ import android.net.WifiKey;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
import android.os.HandlerThread;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
@@ -44,7 +45,6 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
/** Unit tests for {@link WifiNetworkScoreCache}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 856f0c79a2e7..2a8df8dba1d2 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -26,9 +26,10 @@ import android.net.MacAddress;
import android.net.MatchAllNetworkSpecifier;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 5cc821717462..31f501ff85c1 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -19,7 +19,8 @@ package android.net.wifi;
import static org.junit.Assert.*;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index cf1ed8fa84c0..76bfff065f4d 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -32,7 +32,8 @@ import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Handler;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.internal.util.test.BidirectionalAsyncChannelServer;
@@ -44,7 +45,6 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index b58f2c76dab9..10a37c09f500 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -19,7 +19,7 @@ package android.net.wifi;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
index 6ecd93114dc2..83affed0b4e0 100644
--- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -18,7 +18,7 @@ package android.net.wifi.aware;
import static org.hamcrest.core.IsEqual.equalTo;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 657e5a70b53d..4189e40718d8 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -21,7 +21,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 45e17201bd1f..e882b6bb7c1d 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -38,7 +38,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.util.HexEncoding;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index f32fe599cd95..d9a1d9afff61 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -22,7 +22,10 @@ import static org.junit.Assert.assertTrue;
import android.net.wifi.FakeKeys;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
@@ -30,8 +33,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
-import org.junit.Test;
-
/**
* Unit tests for {@link android.net.wifi.hotspot2.ConfigParser}.
*/
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index 89ecd0fd561b..c7e009ee9864 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -23,7 +23,8 @@ import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiSsid;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index ee5a75ebe1b8..fc03e7eb6176 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -25,9 +25,10 @@ import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
index 707b64f780c5..66c595f92861 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
@@ -19,14 +19,13 @@ package android.net.wifi.hotspot2.omadm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.net.wifi.hotspot2.omadm.PpsMoParser;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
-import android.support.test.filters.SmallTest;
-import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
index ef478c7a105c..85d0a909a230 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -20,7 +20,8 @@ import static org.junit.Assert.assertTrue;
import android.net.wifi.hotspot2.omadm.XMLNode;
import android.net.wifi.hotspot2.omadm.XMLParser;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c07db6c323a2..a9d4b8f008f6 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -22,7 +22,8 @@ import static org.junit.Assert.assertTrue;
import android.net.wifi.EAPConstants;
import android.net.wifi.FakeKeys;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index c7993e308d17..93d471ab6b81 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -20,7 +20,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
index 171d6ff0781f..980b199c04b8 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -20,15 +20,15 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
index 2a7526bc3de6..0b8cd3d63c77 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -20,17 +20,14 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
/**
* Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 80f00a4ccfa3..f61e6b759085 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -19,7 +19,7 @@ package android.net.wifi.p2p;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index 2132b418ffac..9e8dca4057f2 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -21,7 +21,8 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.junit.util.ResourceLeakageDetector;
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 8997ae9daf1d..afc7dff67a1b 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -31,7 +31,8 @@ import android.net.wifi.aware.PeerHandle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;