summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rw-r--r--Android.bp5
-rw-r--r--apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java18
-rw-r--r--apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl1
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java20
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java229
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java43
-rw-r--r--api/current.txt51
-rw-r--r--api/system-current.txt20
-rw-r--r--api/test-current.txt10
-rw-r--r--cmds/idmap2/Android.bp6
-rw-r--r--cmds/idmap2/idmap2/Create.cpp4
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp39
-rw-r--r--cmds/idmap2/idmap2/Scan.cpp2
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp4
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h19
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h132
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h19
-rw-r--r--cmds/idmap2/include/idmap2/Xml.h49
-rw-r--r--cmds/idmap2/include/idmap2/XmlParser.h147
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp218
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp411
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp80
-rw-r--r--cmds/idmap2/libidmap2/Xml.cpp80
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp163
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp5
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp13
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp5
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp7
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp322
-rw-r--r--cmds/idmap2/tests/XmlParserTests.cpp174
-rw-r--r--cmds/idmap2/tests/XmlTests.cpp68
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifest.xml5
-rwxr-xr-xcmds/idmap2/tests/data/overlay/build2
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name-static.apkbin1643 -> 3477 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name.apkbin1599 -> 3389 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-1.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-2.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay.apkbin1595 -> 3489 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays.xml23
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml21
-rw-r--r--cmds/idmap2/tests/data/target/res/xml/test.xml19
-rw-r--r--cmds/idmap2/tests/data/target/target-no-overlayable.apkbin2263 -> 2749 bytes
-rw-r--r--cmds/idmap2/tests/data/target/target.apkbin5017 -> 5017 bytes
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp7
-rw-r--r--core/java/android/app/ActivityView.java6
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl1
-rw-r--r--core/java/android/app/ResourcesManager.java693
-rw-r--r--core/java/android/content/Context.java27
-rw-r--r--core/java/android/content/ContextWrapper.java6
-rw-r--r--core/java/android/content/om/OverlayManager.java5
-rw-r--r--core/java/android/content/res/ApkAssets.java122
-rw-r--r--core/java/android/content/res/AssetManager.java145
-rw-r--r--core/java/android/content/res/Resources.java177
-rw-r--r--core/java/android/content/res/ResourcesImpl.java47
-rw-r--r--core/java/android/content/res/StringBlock.java6
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java16
-rw-r--r--core/java/android/content/res/loader/DirectoryResourceLoader.java74
-rw-r--r--core/java/android/content/res/loader/ResourceLoader.java116
-rw-r--r--core/java/android/content/res/loader/ResourceLoaderManager.java189
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java139
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java11
-rw-r--r--core/java/android/provider/MediaStore.java23
-rw-r--r--core/java/android/view/InputWindowHandle.java10
-rw-r--r--core/java/android/view/SurfaceControl.java23
-rw-r--r--core/java/android/view/View.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java54
-rw-r--r--core/java/android/view/WindowManager.java14
-rw-r--r--core/java/android/view/inspector/OWNERS2
-rw-r--r--core/java/android/webkit/FindAddress.java6
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java46
-rw-r--r--core/java/com/android/internal/app/procstats/AssociationState.java473
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java157
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl (renamed from services/core/java/com/android/server/wm/WindowHashMap.java)17
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.java95
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl21
-rw-r--r--core/java/com/android/internal/compat/OWNERS7
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp74
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp82
-rw-r--r--core/jni/android_os_Debug.cpp4
-rw-r--r--core/jni/android_util_AssetManager.cpp38
-rw-r--r--core/res/res/layout/resolve_list_item.xml20
-rw-r--r--core/res/res/layout/resolver_different_item_header.xml16
-rw-r--r--core/res/res/layout/resolver_list.xml63
-rw-r--r--core/res/res/layout/resolver_list_with_default.xml76
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/dimens.xml12
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/ResourceLoaderTests/Android.bp63
-rw-r--r--core/tests/ResourceLoaderTests/AndroidManifest.xml42
-rw-r--r--core/tests/ResourceLoaderTests/AndroidTest.xml37
-rw-r--r--core/tests/ResourceLoaderTests/NonAsset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/assets/Asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jarbin0 -> 689094 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-reflect.jarbin0 -> 2796397 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jarbin0 -> 2453 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jarbin0 -> 3125 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jarbin0 -> 5568 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jarbin0 -> 13827 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jarbin0 -> 415274 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jarbin0 -> 1290543 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jarbin0 -> 3136 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-test.jarbin0 -> 30966 bytes
-rw-r--r--core/tests/ResourceLoaderTests/overlay/Android.bp20
-rw-r--r--core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/overlay/res/values/strings.xml22
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml21
-rw-r--r--core/tests/ResourceLoaderTests/res/layout/layout.xml23
-rw-r--r--core/tests/ResourceLoaderTests/res/values/strings.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml26
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml27
-rwxr-xr-xcore/tests/ResourceLoaderTests/resources/compileAndLink.sh108
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.pngbin0 -> 1445 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/string_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/string_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/splits/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt101
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt169
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt236
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt183
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt153
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt226
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt354
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt72
-rw-r--r--data/etc/car/com.google.android.car.kitchensink.xml13
-rw-r--r--data/etc/hiddenapi-package-whitelist.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java67
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java80
-rw-r--r--libs/androidfw/Android.bp5
-rw-r--r--libs/androidfw/ApkAssets.cpp93
-rw-r--r--libs/androidfw/Asset.cpp20
-rw-r--r--libs/androidfw/AssetManager2.cpp72
-rw-r--r--libs/androidfw/LoadedArsc.cpp29
-rw-r--r--libs/androidfw/ResourceTypes.cpp5
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h38
-rw-r--r--libs/androidfw/include/androidfw/Asset.h5
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h8
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h26
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h2
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp34
-rw-r--r--libs/androidfw/tests/data/loader/resources.arscbin0 -> 620 bytes
-rw-r--r--libs/androidfw/tests/data/system/R.h6
-rw-r--r--location/java/android/location/Location.java8
-rw-r--r--media/java/android/media/MediaCodec.java15
-rw-r--r--media/java/android/media/MediaCodecInfo.java9
-rw-r--r--media/java/android/media/MediaFormat.java9
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java6
-rw-r--r--media/java/android/media/ThumbnailUtils.java18
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java10
-rw-r--r--packages/PackageInstaller/res/values-television/themes.xml31
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java8
-rw-r--r--packages/SystemUI/proguard.flags4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierTextController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/ServiceBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIBinder.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/VendorServices.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipUI.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java146
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java (renamed from services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java)37
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java8
-rw-r--r--services/core/java/com/android/server/ContextHubSystemService.java8
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java2
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java19
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java2
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java2
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java38
-rw-r--r--services/core/java/com/android/server/compat/OWNERS7
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java12
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java68
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java2
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluator.java78
-rw-r--r--services/core/java/com/android/server/integrity/model/AtomicFormula.java84
-rw-r--r--services/core/java/com/android/server/integrity/model/Formula.java2
-rw-r--r--services/core/java/com/android/server/integrity/model/OpenFormula.java21
-rw-r--r--services/core/java/com/android/server/integrity/model/Rule.java20
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java43
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java14
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java10
-rw-r--r--services/core/java/com/android/server/os/SchedulingPolicyService.java3
-rw-r--r--services/core/java/com/android/server/os/TEST_MAPPING7
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java63
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java45
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java9
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java24
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java4
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java18
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/DragState.java4
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java4
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java72
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java)50
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java183
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java17
-rw-r--r--telephony/java/android/telephony/CellInfoNr.java4
-rw-r--r--telephony/java/android/telephony/ICellInfoCallback.aidl3
-rw-r--r--telephony/java/android/telephony/SmsManager.java30
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java88
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java21
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java144
-rw-r--r--test-mock/src/android/test/mock/MockContext.java6
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java1
354 files changed, 9224 insertions, 1812 deletions
diff --git a/.mailmap b/.mailmap
index b061ccf9e467..40c295ee59a5 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1 +1 @@
-Ember Rose <emberr@google.com> <ashleyrose@google.com>
+Ember Rose <emberrose@google.com> <ashleyrose@google.com>
diff --git a/Android.bp b/Android.bp
index 1eba3e2711f1..e2a42195cba1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -413,6 +413,7 @@ java_library {
"framework-platform-compat-config",
"libcore-platform-compat-config",
"services-platform-compat-config",
+ "media-provider-platform-compat-config",
],
sdk_version: "core_platform",
}
@@ -452,7 +453,9 @@ java_library {
srcs: [
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
- ":unsupportedappusage_annotation_files",
+ ],
+ static_libs: [
+ "art.module.api.annotations",
],
sdk_version: "core_current",
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 9039f921b3ba..e27670c34fb2 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -17,10 +17,13 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import java.util.List;
+
/**
* Access to the service that keeps track of device idleness and drives low power mode based on
* that.
@@ -66,4 +69,19 @@ public class DeviceIdleManager {
return new String[0];
}
}
+
+ /**
+ * Add the specified packages to the power save whitelist.
+ *
+ * @return the number of packages that were successfully added to the whitelist
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public int addPowerSaveWhitelistApps(@NonNull List<String> packageNames) {
+ try {
+ return mService.addPowerSaveWhitelistApps(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return 0;
+ }
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 9d5becbf77cd..20fb000b36d3 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -21,6 +21,7 @@ import android.os.UserHandle;
/** @hide */
interface IDeviceIdleController {
void addPowerSaveWhitelistApp(String name);
+ int addPowerSaveWhitelistApps(in List<String> packageNames);
void removePowerSaveWhitelistApp(String name);
/* Removes an app from the system whitelist. Calling restoreSystemPowerWhitelistApp will add
the app back into the system whitelist */
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 127324936e09..6475f5706a6d 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -49,4 +49,24 @@ public interface DeviceIdleInternal {
int[] getPowerSaveWhitelistUserAppIds();
int[] getPowerSaveTempWhitelistAppIds();
+
+ /**
+ * Listener to be notified when DeviceIdleController determines that the device has moved or is
+ * stationary.
+ */
+ interface StationaryListener {
+ void onDeviceStationaryChanged(boolean isStationary);
+ }
+
+ /**
+ * Registers a listener that will be notified when the system has detected that the device is
+ * stationary or in motion.
+ */
+ void registerStationaryListener(StationaryListener listener);
+
+ /**
+ * Unregisters a registered stationary listener from being notified when the system has detected
+ * that the device is stationary or in motion.
+ */
+ void unregisterStationaryListener(StationaryListener listener);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 3dc9a28ac965..4ee46f453bca 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -105,6 +105,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.stream.Collectors;
/**
@@ -274,6 +276,7 @@ public class DeviceIdleController extends SystemService
private IBatteryStats mBatteryStats;
private ActivityManagerInternal mLocalActivityManager;
private ActivityTaskManagerInternal mLocalActivityTaskManager;
+ private DeviceIdleInternal mLocalService;
private PowerManagerInternal mLocalPowerManager;
private PowerManager mPowerManager;
private INetworkPolicyManager mNetworkPolicyManager;
@@ -288,6 +291,7 @@ public class DeviceIdleController extends SystemService
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mQuickDozeActivated;
+ private boolean mQuickDozeActivatedWhileIdling;
private boolean mForceIdle;
private boolean mNetworkConnected;
private boolean mScreenOn;
@@ -299,6 +303,10 @@ public class DeviceIdleController extends SystemService
private boolean mHasNetworkLocation;
private Location mLastGenericLocation;
private Location mLastGpsLocation;
+
+ /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+ private long mLastMotionEventElapsed;
+
// Current locked state of the screen
private boolean mScreenLocked;
private int mNumBlockingConstraints = 0;
@@ -548,6 +556,9 @@ public class DeviceIdleController extends SystemService
*/
private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+ private final ArraySet<DeviceIdleInternal.StationaryListener> mStationaryListeners =
+ new ArraySet<>();
+
private static final int EVENT_NULL = 0;
private static final int EVENT_NORMAL = 1;
private static final int EVENT_LIGHT_IDLE = 2;
@@ -606,6 +617,22 @@ public class DeviceIdleController extends SystemService
}
};
+
+ private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
+ synchronized (DeviceIdleController.this) {
+ if (!isStationaryLocked()) {
+ // If the device keeps registering motion, then the alarm should be
+ // rescheduled, so this shouldn't go off until the device is stationary.
+ // This case may happen in a race condition (alarm goes off right before
+ // motion is detected, but handleMotionDetectedLocked is called before
+ // we enter this block).
+ Slog.w(TAG, "motion timeout went off and device isn't stationary");
+ return;
+ }
+ }
+ postStationaryStatusUpdated();
+ };
+
private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
@@ -655,12 +682,70 @@ public class DeviceIdleController extends SystemService
}
};
+ /** Post stationary status only to this listener. */
+ private void postStationaryStatus(DeviceIdleInternal.StationaryListener listener) {
+ mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
+ }
+
+ /** Post stationary status to all registered listeners. */
+ private void postStationaryStatusUpdated() {
+ mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
+ }
+
+ private boolean isStationaryLocked() {
+ final long now = mInjector.getElapsedRealtime();
+ return mMotionListener.active
+ // Listening for motion for long enough and last motion was long enough ago.
+ && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
+ >= mConstants.MOTION_INACTIVE_TIMEOUT;
+ }
+
+ @VisibleForTesting
+ void registerStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+ synchronized (this) {
+ if (!mStationaryListeners.add(listener)) {
+ // Listener already registered.
+ return;
+ }
+ postStationaryStatus(listener);
+ if (mMotionListener.active) {
+ if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
+ // First listener to be registered and the device isn't stationary, so we
+ // need to register the alarm to report the device is stationary.
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ } else {
+ startMonitoringMotionLocked();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ }
+ }
+
+ private void unregisterStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+ synchronized (this) {
+ if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
+ // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
+ // and so doesn't need to be on for ACTIVE or INACTIVE states.
+ // Motion detection isn't needed when idling due to Quick Doze.
+ && (mState == STATE_ACTIVE || mState == STATE_INACTIVE
+ || mQuickDozeActivated)) {
+ maybeStopMonitoringMotionLocked();
+ }
+ }
+ }
+
@VisibleForTesting
final class MotionListener extends TriggerEventListener
implements SensorEventListener {
boolean active = false;
+ /**
+ * Time in the elapsed realtime timebase when this listener was activated. Only valid if
+ * {@link #active} is true.
+ */
+ long activatedTimeElapsed;
+
public boolean isActive() {
return active;
}
@@ -668,7 +753,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- active = false;
motionLocked();
}
}
@@ -676,8 +760,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onSensorChanged(SensorEvent event) {
synchronized (DeviceIdleController.this) {
- mSensorManager.unregisterListener(this, mMotionSensor);
- active = false;
motionLocked();
}
}
@@ -695,6 +777,7 @@ public class DeviceIdleController extends SystemService
}
if (success) {
active = true;
+ activatedTimeElapsed = mInjector.getElapsedRealtime();
} else {
Slog.e(TAG, "Unable to register for " + mMotionSensor);
}
@@ -1302,6 +1385,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_REPORT_IDLE_OFF = 4;
private static final int MSG_REPORT_ACTIVE = 5;
private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+ @VisibleForTesting
+ static final int MSG_REPORT_STATIONARY_STATUS = 7;
private static final int MSG_FINISH_IDLE_OP = 8;
private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
@@ -1427,6 +1512,32 @@ public class DeviceIdleController extends SystemService
updatePreIdleFactor();
maybeDoImmediateMaintenance();
} break;
+ case MSG_REPORT_STATIONARY_STATUS: {
+ final DeviceIdleInternal.StationaryListener newListener =
+ (DeviceIdleInternal.StationaryListener) msg.obj;
+ final DeviceIdleInternal.StationaryListener[] listeners;
+ final boolean isStationary;
+ synchronized (DeviceIdleController.this) {
+ isStationary = isStationaryLocked();
+ if (newListener == null) {
+ // Only notify all listeners if we aren't directing to one listener.
+ listeners = mStationaryListeners.toArray(
+ new DeviceIdleInternal.StationaryListener[
+ mStationaryListeners.size()]);
+ } else {
+ listeners = null;
+ }
+ }
+ if (listeners != null) {
+ for (DeviceIdleInternal.StationaryListener listener : listeners) {
+ listener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ if (newListener != null) {
+ newListener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ break;
}
}
}
@@ -1440,11 +1551,20 @@ public class DeviceIdleController extends SystemService
if (DEBUG) {
Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")");
}
+ addPowerSaveWhitelistApps(Collections.singletonList(name));
+ }
+
+ @Override
+ public int addPowerSaveWhitelistApps(List<String> packageNames) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "addPowerSaveWhitelistApps(name = " + packageNames + ")");
+ }
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
long ident = Binder.clearCallingIdentity();
try {
- addPowerSaveWhitelistAppInternal(name);
+ return addPowerSaveWhitelistAppsInternal(packageNames);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1679,6 +1799,16 @@ public class DeviceIdleController extends SystemService
public int[] getPowerSaveTempWhitelistAppIds() {
return DeviceIdleController.this.getAppIdTempWhitelistInternal();
}
+
+ @Override
+ public void registerStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.registerStationaryListener(listener);
+ }
+
+ @Override
+ public void unregisterStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.unregisterStationaryListener(listener);
+ }
}
static class Injector {
@@ -1862,7 +1992,8 @@ public class DeviceIdleController extends SystemService
mBinderService = new BinderService();
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
- publishLocalService(DeviceIdleInternal.class, new LocalService());
+ mLocalService = new LocalService();
+ publishLocalService(DeviceIdleInternal.class, mLocalService);
}
@Override
@@ -2068,21 +2199,35 @@ public class DeviceIdleController extends SystemService
}
}
- public boolean addPowerSaveWhitelistAppInternal(String name) {
+ private int addPowerSaveWhitelistAppsInternal(List<String> pkgNames) {
+ int numAdded = 0;
+ int numErrors = 0;
synchronized (this) {
- try {
- ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
- PackageManager.MATCH_ANY_USER);
- if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) {
- reportPowerSaveWhitelistChangedLocked();
- updateWhitelistAppIdsLocked();
- writeConfigFileLocked();
+ for (int i = pkgNames.size() - 1; i >= 0; --i) {
+ final String name = pkgNames.get(i);
+ if (name == null) {
+ numErrors++;
+ continue;
}
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
+ try {
+ ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
+ PackageManager.MATCH_ANY_USER);
+ if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid))
+ == null) {
+ numAdded++;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Tried to add unknown package to power save whitelist: " + name);
+ numErrors++;
+ }
+ }
+ if (numAdded > 0) {
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
}
}
+ return pkgNames.size() - numErrors;
}
public boolean removePowerSaveWhitelistAppInternal(String name) {
@@ -2593,6 +2738,8 @@ public class DeviceIdleController extends SystemService
void updateQuickDozeFlagLocked(boolean enabled) {
if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
mQuickDozeActivated = enabled;
+ mQuickDozeActivatedWhileIdling =
+ mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
if (enabled) {
// If Quick Doze is enabled, see if we should go straight into it.
becomeInactiveIfAppropriateLocked();
@@ -2777,10 +2924,11 @@ public class DeviceIdleController extends SystemService
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
mIdleStartTime = 0;
+ mQuickDozeActivatedWhileIdling = false;
cancelAlarmLocked();
cancelSensingTimeoutAlarmLocked();
cancelLocatingLocked();
- stopMonitoringMotionLocked();
+ maybeStopMonitoringMotionLocked();
mAnyMotionDetector.stop();
updateActiveConstraintsLocked();
}
@@ -3267,11 +3415,23 @@ public class DeviceIdleController extends SystemService
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
- // The motion sensor will have been disabled at this point
+ mLastMotionEventElapsed = mInjector.getElapsedRealtime();
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
+ if (mStationaryListeners.size() > 0) {
+ postStationaryStatusUpdated();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
+ // Don't exit idle due to motion if quick doze is enabled.
+ // However, if the device started idling due to the normal progression (going through
+ // all the states) and then had quick doze activated, come out briefly on motion so the
+ // user can get slightly fresher content.
+ return;
+ }
+ maybeStopMonitoringMotionLocked();
// The device is not yet active, so we want to go back to the pending idle
// state to wait again for no motion. Note that we only monitor for motion
// after moving out of the inactive state, so no need to worry about that.
@@ -3323,10 +3483,15 @@ public class DeviceIdleController extends SystemService
}
}
- void stopMonitoringMotionLocked() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
- if (mMotionSensor != null && mMotionListener.active) {
+ /**
+ * Stops motion monitoring. Will not stop monitoring if there are registered stationary
+ * listeners.
+ */
+ private void maybeStopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
mMotionListener.unregisterLocked();
+ cancelMotionTimeoutAlarmLocked();
}
}
@@ -3353,6 +3518,10 @@ public class DeviceIdleController extends SystemService
}
}
+ private void cancelMotionTimeoutAlarmLocked() {
+ mAlarmManager.cancel(mMotionTimeoutAlarmListener);
+ }
+
void cancelSensingTimeoutAlarmLocked() {
if (mNextSensingTimeoutAlarmTime != 0) {
mNextSensingTimeoutAlarmTime = 0;
@@ -3393,6 +3562,14 @@ public class DeviceIdleController extends SystemService
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
}
+ private void scheduleMotionTimeoutAlarmLocked() {
+ if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
+ long nextMotionTimeoutAlarmTime =
+ mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ }
+
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -3918,7 +4095,8 @@ public class DeviceIdleController extends SystemService
char op = arg.charAt(0);
String pkg = arg.substring(1);
if (op == '+') {
- if (addPowerSaveWhitelistAppInternal(pkg)) {
+ if (addPowerSaveWhitelistAppsInternal(Collections.singletonList(pkg))
+ == 1) {
pw.println("Added: " + pkg);
} else {
pw.println("Unknown package: " + pkg);
@@ -4310,9 +4488,14 @@ public class DeviceIdleController extends SystemService
}
pw.println(" }");
}
- if (mUseMotionSensor) {
+ if (mUseMotionSensor || mStationaryListeners.size() > 0) {
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mNotMoving="); pw.println(mNotMoving);
+ pw.print(" mMotionListener.activatedTimeElapsed=");
+ pw.println(mMotionListener.activatedTimeElapsed);
+ pw.print(" mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
+ pw.print(" "); pw.print(mStationaryListeners.size());
+ pw.println(" stationary listeners registered");
}
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 7273ea71f3ab..14d5a683ce83 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1025,17 +1025,18 @@ public class JobSchedulerService extends com.android.server.SystemService
// This may throw a SecurityException.
jobStatus.prepareLocked();
- if (work != null) {
- // If work has been supplied, enqueue it into the new job.
- jobStatus.enqueueWorkLocked(work);
- }
-
if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance
cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
} else {
startTrackingJobLocked(jobStatus, null);
}
+
+ if (work != null) {
+ // If work has been supplied, enqueue it into the new job.
+ jobStatus.enqueueWorkLocked(work);
+ }
+
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
uId, null, jobStatus.getBatteryName(),
StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 782e6463d845..26db4a30ebda 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -251,7 +251,7 @@ public final class JobServiceContext implements ServiceConnection {
binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_NOT_PERCEPTIBLE,
- new UserHandle(job.getUserId()));
+ UserHandle.of(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
// android:singleUser, can result in a SecurityException being thrown from
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index b97da59f8d17..aa7696df6dbd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -17,13 +17,8 @@
package com.android.server.job.restrictions;
import android.app.job.JobParameters;
-import android.content.Context;
-import android.os.IThermalService;
-import android.os.IThermalStatusListener;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Temperature;
-import android.util.Slog;
+import android.os.PowerManager;
+import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
@@ -36,31 +31,29 @@ public class ThermalStatusRestriction extends JobRestriction {
private volatile boolean mIsThermalRestricted = false;
+ private PowerManager mPowerManager;
+
public ThermalStatusRestriction(JobSchedulerService service) {
super(service, JobParameters.REASON_DEVICE_THERMAL);
}
@Override
public void onSystemServicesReady() {
- final IThermalService thermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- if (thermalService != null) {
- try {
- thermalService.registerThermalStatusListener(new IThermalStatusListener.Stub() {
- @Override
- public void onStatusChange(int status) {
- final boolean shouldBeActive = status >= Temperature.THROTTLING_SEVERE;
- if (mIsThermalRestricted == shouldBeActive) {
- return;
- }
- mIsThermalRestricted = shouldBeActive;
- mService.onControllerStateChanged();
- }
- });
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal callback.", e);
+ mPowerManager = mService.getContext().getSystemService(PowerManager.class);
+ // Use MainExecutor
+ mPowerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
+ @Override
+ public void onThermalStatusChanged(int status) {
+ // This is called on the main thread. Do not do any slow operations in it.
+ // mService.onControllerStateChanged() will just post a message, which is okay.
+ final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
+ if (mIsThermalRestricted == shouldBeActive) {
+ return;
+ }
+ mIsThermalRestricted = shouldBeActive;
+ mService.onControllerStateChanged();
}
- }
+ });
}
@Override
diff --git a/api/current.txt b/api/current.txt
index e6e2b40cbb19..603dec283c66 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9805,7 +9805,7 @@ package android.content {
method public abstract java.io.File[] getExternalCacheDirs();
method @Nullable public abstract java.io.File getExternalFilesDir(@Nullable String);
method public abstract java.io.File[] getExternalFilesDirs(String);
- method public abstract java.io.File[] getExternalMediaDirs();
+ method @Deprecated public abstract java.io.File[] getExternalMediaDirs();
method public abstract java.io.File getFileStreamPath(String);
method public abstract java.io.File getFilesDir();
method public java.util.concurrent.Executor getMainExecutor();
@@ -12402,6 +12402,8 @@ package android.content.res {
public class Resources {
ctor @Deprecated public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
+ method public void addLoader(@NonNull android.content.res.loader.ResourceLoader, @NonNull android.content.res.loader.ResourcesProvider, @IntRange(from=0) int);
+ method public int addLoader(@NonNull android.content.res.loader.ResourceLoader, @NonNull android.content.res.loader.ResourcesProvider);
method public final void finishPreloading();
method public final void flushLayoutCache();
method @NonNull public android.content.res.XmlResourceParser getAnimation(@AnimatorRes @AnimRes int) throws android.content.res.Resources.NotFoundException;
@@ -12428,6 +12430,7 @@ package android.content.res {
method @NonNull public int[] getIntArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException;
method public int getInteger(@IntegerRes int) throws android.content.res.Resources.NotFoundException;
method @NonNull public android.content.res.XmlResourceParser getLayout(@LayoutRes int) throws android.content.res.Resources.NotFoundException;
+ method @NonNull public java.util.List<android.util.Pair<android.content.res.loader.ResourceLoader,android.content.res.loader.ResourcesProvider>> getLoaders();
method @Deprecated public android.graphics.Movie getMovie(@RawRes int) throws android.content.res.Resources.NotFoundException;
method @NonNull public String getQuantityString(@PluralsRes int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
method @NonNull public String getQuantityString(@PluralsRes int, int) throws android.content.res.Resources.NotFoundException;
@@ -12455,6 +12458,8 @@ package android.content.res {
method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException;
method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public int removeLoader(@NonNull android.content.res.loader.ResourceLoader);
+ method public void setLoaders(@Nullable java.util.List<android.util.Pair<android.content.res.loader.ResourceLoader,android.content.res.loader.ResourcesProvider>>);
method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
field @AnyRes public static final int ID_NULL = 0; // 0x0
}
@@ -12522,6 +12527,33 @@ package android.content.res {
}
+package android.content.res.loader {
+
+ public class DirectoryResourceLoader implements android.content.res.loader.ResourceLoader {
+ ctor public DirectoryResourceLoader(@NonNull java.io.File);
+ method @Nullable public java.io.File findFile(@NonNull String);
+ method @NonNull public java.io.File getDirectory();
+ }
+
+ public interface ResourceLoader {
+ method @Nullable public default java.io.InputStream loadAsset(@NonNull String, int) throws java.io.IOException;
+ method @Nullable public default android.os.ParcelFileDescriptor loadAssetFd(@NonNull String) throws java.io.IOException;
+ method @Nullable public default android.graphics.drawable.Drawable loadDrawable(@NonNull android.util.TypedValue, int, int, @Nullable android.content.res.Resources.Theme);
+ method @Nullable public default android.content.res.XmlResourceParser loadXmlResourceParser(@NonNull String, @AnyRes int);
+ }
+
+ public final class ResourcesProvider implements java.lang.AutoCloseable java.io.Closeable {
+ method public void close();
+ method @NonNull public static android.content.res.loader.ResourcesProvider empty();
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.SharedMemory) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromArsc(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromArsc(@NonNull android.os.SharedMemory) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromSplit(@NonNull android.content.Context, @NonNull String) throws java.io.IOException;
+ }
+
+}
+
package android.database {
public abstract class AbstractCursor implements android.database.CrossProcessCursor {
@@ -24356,6 +24388,7 @@ package android.media {
field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
field public static final String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+ field public static final String PARAMETER_KEY_LOW_LATENCY = "low-latency";
field public static final String PARAMETER_KEY_OFFSET_TIME = "time-offset-us";
field public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
field public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames";
@@ -24529,6 +24562,7 @@ package android.media {
field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
field public static final String FEATURE_FrameParsing = "frame-parsing";
field public static final String FEATURE_IntraRefresh = "intra-refresh";
+ field public static final String FEATURE_LowLatency = "low-latency";
field public static final String FEATURE_MultipleFrames = "multiple-frames";
field public static final String FEATURE_PartialFrame = "partial-frame";
field public static final String FEATURE_SecurePlayback = "secure-playback";
@@ -25239,6 +25273,7 @@ package android.media {
field public static final String KEY_LANGUAGE = "language";
field public static final String KEY_LATENCY = "latency";
field public static final String KEY_LEVEL = "level";
+ field public static final String KEY_LOW_LATENCY = "low-latency";
field public static final String KEY_MAX_B_FRAMES = "max-bframes";
field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
field public static final String KEY_MAX_HEIGHT = "max-height";
@@ -25412,6 +25447,9 @@ package android.media {
field public static final int METADATA_KEY_BITRATE = 20; // 0x14
field public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; // 0x19
field public static final int METADATA_KEY_CD_TRACK_NUMBER = 0; // 0x0
+ field public static final int METADATA_KEY_COLOR_RANGE = 37; // 0x25
+ field public static final int METADATA_KEY_COLOR_STANDARD = 35; // 0x23
+ field public static final int METADATA_KEY_COLOR_TRANSFER = 36; // 0x24
field public static final int METADATA_KEY_COMPILATION = 15; // 0xf
field public static final int METADATA_KEY_COMPOSER = 4; // 0x4
field public static final int METADATA_KEY_DATE = 5; // 0x5
@@ -38749,6 +38787,9 @@ package android.provider {
field public static final String ARTIST = "artist";
field public static final String BOOKMARK = "bookmark";
field public static final String CATEGORY = "category";
+ field public static final String COLOR_RANGE = "color_range";
+ field public static final String COLOR_STANDARD = "color_standard";
+ field public static final String COLOR_TRANSFER = "color_transfer";
field public static final String DESCRIPTION = "description";
field public static final String IS_PRIVATE = "isprivate";
field public static final String LANGUAGE = "language";
@@ -45087,6 +45128,7 @@ package android.telephony {
method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
+ method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
@@ -45119,7 +45161,7 @@ package android.telephony {
method public String getNetworkOperatorName();
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
- method public int getPhoneCount();
+ method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
@@ -45135,6 +45177,7 @@ package android.telephony {
method public int getSimState();
method public int getSimState(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId();
+ method public int getSupportedModemCount();
method @Nullable public String getTypeAllocationCode();
method @Nullable public String getTypeAllocationCode(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @NonNull public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo();
@@ -45239,6 +45282,10 @@ package android.telephony {
field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
+ field public static final int MODEM_COUNT_DUAL_MODEM = 2; // 0x2
+ field public static final int MODEM_COUNT_NO_MODEM = 0; // 0x0
+ field public static final int MODEM_COUNT_SINGLE_MODEM = 1; // 0x1
+ field public static final int MODEM_COUNT_TRI_MODEM = 3; // 0x3
field public static final int MULTISIM_ALLOWED = 0; // 0x0
field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2
field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 22da4d6b9916..92315368d182 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -226,6 +226,7 @@ package android {
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int resourcesMap = 16844297; // 0x1010609
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -1358,8 +1359,9 @@ package android.content {
public abstract class Context {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
method public abstract android.content.Context createCredentialProtectedStorageContext();
- method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract java.io.File getPreloadsFileCache();
method public abstract boolean isCredentialProtectedStorage();
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
@@ -3418,7 +3420,9 @@ package android.location {
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
method public void setIsFromMockProvider(boolean);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
@@ -9574,17 +9578,17 @@ package android.telephony.ims.stub {
public class ImsSmsImplBase {
ctor public ImsSmsImplBase();
- method public void acknowledgeSms(int, int, int);
- method public void acknowledgeSmsReport(int, int, int);
+ method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
+ method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
method public String getSmsFormat();
method public void onReady();
- method @Deprecated public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultError(int, int, int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultSuccess(int, int) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException;
method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method @Deprecated public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException;
method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method public void sendSms(int, int, String, String, boolean, byte[]);
+ method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]);
field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 70837a84adfd..d292e0173761 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -131,7 +131,7 @@ package android.app {
method public void startActivity(@NonNull android.content.Intent);
method public void startActivity(@NonNull android.content.Intent, android.os.UserHandle);
method public void startActivity(@NonNull android.app.PendingIntent);
- method public void startActivity(@NonNull android.app.PendingIntent, @NonNull android.app.ActivityOptions);
+ method public void startActivity(@NonNull android.app.PendingIntent, @Nullable android.content.Intent, @NonNull android.app.ActivityOptions);
}
public abstract static class ActivityView.StateCallback {
@@ -659,7 +659,8 @@ package android.content {
}
public abstract class Context {
- method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+ method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.view.Display getDisplay();
method public abstract int getDisplayId();
method public android.os.UserHandle getUser();
@@ -787,7 +788,9 @@ package android.content.res {
public final class AssetManager implements java.lang.AutoCloseable {
method @NonNull public String[] getApkPaths();
+ method @Nullable public String getLastResourceResolution();
method @Nullable public String getOverlayablesToString(String);
+ method public void setResourceResolutionLoggingEnabled(boolean);
}
public final class Configuration implements java.lang.Comparable<android.content.res.Configuration> android.os.Parcelable {
@@ -1088,6 +1091,8 @@ package android.location {
public class Location implements android.os.Parcelable {
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
@@ -1740,6 +1745,7 @@ package android.os {
}
public class DeviceIdleManager {
+ method @RequiresPermission("android.permission.DEVICE_POWER") public int addPowerSaveWhitelistApps(@NonNull java.util.List<java.lang.String>);
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4c77ba402595..41a17064c3ba 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,9 +43,10 @@ cc_library {
"libidmap2/Policies.cpp",
"libidmap2/PrettyPrintVisitor.cpp",
"libidmap2/RawPrintVisitor.cpp",
+ "libidmap2/ResourceMapping.cpp",
"libidmap2/ResourceUtils.cpp",
"libidmap2/Result.cpp",
- "libidmap2/Xml.cpp",
+ "libidmap2/XmlParser.cpp",
"libidmap2/ZipFile.cpp",
],
export_include_dirs: ["include"],
@@ -97,9 +98,10 @@ cc_test {
"tests/PoliciesTests.cpp",
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
+ "tests/ResourceMappingTests.cpp",
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
- "tests/XmlTests.cpp",
+ "tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
required: [
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index f482191b09a8..3ff6d3514331 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -99,8 +99,8 @@ Result<Unit> Create(const std::vector<std::string>& args) {
return Error("failed to load apk %s", overlay_apk_path.c_str());
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d090cee..c5cf9807b689 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -33,9 +33,10 @@
#include "androidfw/Util.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -57,8 +58,7 @@ using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
-using android::idmap2::Xml;
-using android::idmap2::ZipFile;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::util::Utf16ToUtf8;
namespace {
@@ -132,29 +132,6 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res
return out;
}
-Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
- const auto zip = ZipFile::Open(apk_path);
- if (!zip) {
- return Error("failed to open %s as zip", apk_path.c_str());
- }
- const auto entry = zip->Uncompress("AndroidManifest.xml");
- if (!entry) {
- return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str());
- }
- const auto xml = Xml::Create(entry->buf, entry->size);
- if (!xml) {
- return Error("failed to create XML buffer");
- }
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- return Error("failed to find <overlay> tag");
- }
- const auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- return Error("failed to find targetPackage attribute");
- }
- return iter->second;
-}
} // namespace
Result<Unit> Lookup(const std::vector<std::string>& args) {
@@ -202,12 +179,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
}
apk_assets.push_back(std::move(target_apk));
- const Result<std::string> package_name =
- GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!package_name) {
- return Error("failed to parse android:targetPackage from overlay manifest");
+ auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
+ true /* assert_overlay */);
+ if (!manifest_info) {
+ return manifest_info.GetError();
}
- target_package_name = *package_name;
+ target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(),
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index d0530f2d344e..e643ab567644 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -33,7 +33,7 @@
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::idmap2::CommandLineOptions;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f61520a..4aabf8399a25 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -137,8 +137,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
return error("failed to load apk " + overlay_apk_path);
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, policy_bitmask, enforce_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ffc989d..f2cae58b910a 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -56,20 +56,14 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
namespace android::idmap2 {
class Idmap;
class Visitor;
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId; // 0xpptteeee
-typedef uint8_t PackageId; // pp in 0xpptteeee
-typedef uint8_t TypeId; // tt in 0xpptteeee
-typedef uint16_t EntryId; // eeee in 0xpptteeee
-
static constexpr const ResourceId kPadding = 0xffffffffu;
-
static constexpr const EntryId kNoEntry = 0xffffu;
// magic number: all idmap files start with this
@@ -155,7 +149,7 @@ class IdmapData {
PackageId target_package_id_;
uint16_t type_count_;
- friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(Header);
};
@@ -194,12 +188,15 @@ class IdmapData {
uint16_t entry_offset_;
std::vector<EntryId> entries_;
- friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(TypeEntry);
};
static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+ static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
+ const ResourceMapping& resource_mapping);
+
inline const std::unique_ptr<const Header>& GetHeader() const {
return header_;
}
@@ -232,9 +229,7 @@ class Idmap {
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
- static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+ static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 000000000000..c3e1ef06c20f
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+namespace android::idmap2 {
+
+struct TargetValue {
+ typedef uint8_t DataType;
+ typedef uint32_t DataValue;
+ DataType data_type;
+ DataValue data_value;
+};
+
+using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using OverlayResourceMap = std::map<ResourceId, ResourceId>;
+
+class ResourceMapping {
+ public:
+ // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
+ // `false` disables all overlayable and policy enforcement: this is intended for backwards
+ // compatibility pre-Q and unit tests.
+ static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable);
+
+ // Retrieves the mapping of target resource id to overlay value.
+ inline TargetResourceMap GetTargetToOverlayMap() const {
+ return target_map_;
+ }
+
+ // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
+ // an overlay resource to appear as a reference to its corresponding target resource at runtime.
+ OverlayResourceMap GetOverlayToTargetMap() const;
+
+ // Retrieves the build-time package id of the target package.
+ inline uint32_t GetTargetPackageId() const {
+ return target_package_id_;
+ }
+
+ // Retrieves the build-time package id of the overlay package.
+ inline uint32_t GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ // Retrieves the offset that was added to the index of inline string overlay values so the indices
+ // do not collide with the indices of the overlay resource table string pool.
+ inline uint32_t GetStringPoolOffset() const {
+ return string_pool_offset_;
+ }
+
+ // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
+ inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
+ return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+ }
+
+ private:
+ ResourceMapping() = default;
+
+ // Apps a mapping of target resource id to the type and value of the data that overlays the
+ // target resource. The data_type is the runtime format of the data value (see
+ // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+ // resource should appear as a reference to its corresponding target resource at runtime.
+ Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
+ TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+
+ // Removes the overlay value mapping for the target resource.
+ void RemoveMapping(ResourceId target_resource);
+
+ // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
+ static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser);
+
+ // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
+ // a target resource, a resource must exist in the overlay with the same type and entry name as
+ // the target resource.
+ static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
+ const AssetManager2* overlay_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package);
+
+ // Removes resources that do not pass policy or overlayable checks of the target package.
+ void FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies);
+
+ TargetResourceMap target_map_;
+ std::multimap<ResourceId, ResourceId> overlay_map_;
+
+ uint32_t target_package_id_ = 0;
+ uint32_t overlay_package_id_ = 0;
+ uint32_t string_pool_offset_ = 0;
+ uint32_t string_pool_data_length_ = 0;
+ std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 9a0c2abced5a..abc2df1a147f 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,17 +21,28 @@
#include <string>
#include "androidfw/AssetManager2.h"
-#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android::idmap2::utils {
+namespace android::idmap2 {
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId; // 0xpptteeee
+typedef uint8_t PackageId; // pp in 0xpptteeee
+typedef uint8_t TypeId; // tt in 0xpptteeee
+typedef uint16_t EntryId; // eeee in 0xpptteeee
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+namespace utils {
struct OverlayManifestInfo {
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
std::string requiredSystemPropertyValue; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
};
@@ -41,6 +52,8 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
-} // namespace android::idmap2::utils
+} // namespace utils
+
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
deleted file mode 100644
index dd89dee0f64f..000000000000
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ /dev/null
@@ -1,49 +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.
- */
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_
-#define IDMAP2_INCLUDE_IDMAP2_XML_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-
-namespace android::idmap2 {
-
-class Xml {
- public:
- static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
-
- std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
-
- ~Xml();
-
- private:
- Xml() {
- }
-
- mutable ResXMLTree xml_;
-
- DISALLOW_COPY_AND_ASSIGN(Xml);
-};
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
new file mode 100644
index 000000000000..972a6d7e3427
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "Result.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android::idmap2 {
+
+class XmlParser {
+ public:
+ using Event = ResXMLParser::event_code_t;
+ class iterator;
+
+ class Node {
+ public:
+ Event event() const;
+ std::string name() const;
+
+ Result<std::string> GetAttributeStringValue(const std::string& name) const;
+ Result<Res_value> GetAttributeValue(const std::string& name) const;
+
+ bool operator==(const Node& rhs) const;
+ bool operator!=(const Node& rhs) const;
+
+ private:
+ explicit Node(const ResXMLTree& tree);
+ Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
+
+ // Retrieves/Sets the position of the position of the xml parser in the xml tree.
+ ResXMLParser::ResXMLPosition get_position() const;
+ void set_position(const ResXMLParser::ResXMLPosition& pos);
+
+ // If `inner_child` is true, seek advances the parser to the first inner child of the current
+ // node. Otherwise, seek advances the parser to the following node. Returns false if there is
+ // no node to seek to.
+ bool Seek(bool inner_child);
+
+ ResXMLParser parser_;
+ friend iterator;
+ };
+
+ class iterator {
+ public:
+ iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
+ }
+
+ inline iterator& operator=(const iterator& rhs) {
+ iter_.set_position(rhs.iter_.get_position());
+ return *this;
+ }
+
+ inline bool operator==(const iterator& rhs) const {
+ return iter_ == rhs.iter_;
+ }
+
+ inline bool operator!=(const iterator& rhs) const {
+ return !(*this == rhs);
+ }
+
+ inline iterator operator++() {
+ // Seek to the following xml node.
+ iter_.Seek(false /* inner_child */);
+ return *this;
+ }
+
+ iterator begin() const {
+ iterator child_it(*this);
+ // Seek to the first inner child of the current node.
+ child_it.iter_.Seek(true /* inner_child */);
+ return child_it;
+ }
+
+ iterator end() const {
+ iterator child_it = begin();
+ while (child_it.iter_.Seek(false /* inner_child */)) {
+ // Continue iterating until the end tag is found.
+ }
+
+ return child_it;
+ }
+
+ inline const Node operator*() {
+ return Node(tree_, iter_.get_position());
+ }
+
+ inline const Node* operator->() {
+ return &iter_;
+ }
+
+ private:
+ explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
+ }
+ iterator(const ResXMLTree& tree, const Node& node)
+ : tree_(tree), iter_(Node(tree, node.get_position())) {
+ }
+
+ const ResXMLTree& tree_;
+ Node iter_;
+ friend XmlParser;
+ };
+
+ // Creates a new xml parser beginning at the first tag.
+ static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
+ bool copy_data = false);
+ ~XmlParser();
+
+ inline iterator tree_iterator() const {
+ return iterator(tree_);
+ }
+
+ inline const ResStringPool& get_strings() const {
+ return tree_.getStrings();
+ }
+
+ private:
+ XmlParser() = default;
+ mutable ResXMLTree tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675965db..389ade59200c 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -30,6 +30,7 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/AssetManager2.h"
+#include "idmap2/ResourceMapping.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
@@ -41,10 +42,6 @@ namespace android::idmap2 {
namespace {
-#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
-
-#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-
class MatchingResources {
public:
void Add(ResourceId target_resid, ResourceId overlay_resid) {
@@ -97,28 +94,6 @@ bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength])
return true;
}
-ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
- return am.GetResourceId(name);
-}
-
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
- if (packages.empty()) {
- return nullptr;
- }
- int id = packages[0]->GetPackageId();
- return loaded_arsc.GetPackageById(id);
-}
-
Result<uint32_t> GetCrc(const ZipFile& zip) {
const Result<uint32_t> a = zip.Crc("resources.arsc");
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
@@ -266,95 +241,57 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea
return {std::move(idmap)};
}
-std::string ConcatPolicies(const std::vector<std::string>& policies) {
- std::string message;
- for (const std::string& policy : policies) {
- if (!message.empty()) {
- message.append("|");
- }
- message.append(policy);
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+ const ResourceMapping& resource_mapping) {
+ if (resource_mapping.GetTargetToOverlayMap().empty()) {
+ return Error("no resources were overlaid");
}
- return message;
-}
-
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
- const utils::OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
- static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
- // If the resource does not have an overlayable definition, allow the resource to be overlaid if
- // the overlay is preinstalled or signed with the same signature as the target.
- if (!target_package.DefinesOverlayable()) {
- return (sDefaultPolicies & fulfilled_policies) != 0
- ? Result<Unit>({})
- : Error(
- "overlay must be preinstalled or signed with the same signature as the "
- "target");
- }
-
- const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
- if (overlayable_info == nullptr) {
- // Do not allow non-overlayable resources to be overlaid.
- return Error("resource has no overlayable declaration");
- }
+ MatchingResources matching_resources;
+ for (const auto mapping : resource_mapping.GetTargetToOverlayMap()) {
+ if (mapping.second.data_type != Res_value::TYPE_REFERENCE) {
+ // The idmap format must change to support non-references.
+ continue;
+ }
- if (overlay_info.target_name != overlayable_info->name) {
- // If the overlay supplies a target overlayable name, the resource must belong to the
- // overlayable defined with the specified name to be overlaid.
- return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
- overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+ matching_resources.Add(mapping.first, mapping.second.data_value);
}
- // Enforce policy restrictions if the resource is declared as overlayable.
- if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
- return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
- ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
- ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+ // encode idmap data
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ const auto types_end = matching_resources.Map().cend();
+ for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
+ auto ei = ti->second.cbegin();
+ std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
+ type->target_type_id_ = EXTRACT_TYPE(ei->first);
+ type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
+ type->entry_offset_ = EXTRACT_ENTRY(ei->first);
+ EntryId last_target_entry = kNoEntry;
+ for (; ei != ti->second.cend(); ++ei) {
+ if (last_target_entry != kNoEntry) {
+ int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
+ type->entries_.insert(type->entries_.end(), count, kNoEntry);
+ }
+ type->entries_.push_back(EXTRACT_ENTRY(ei->second));
+ last_target_entry = EXTRACT_ENTRY(ei->first);
+ }
+ data->type_entries_.push_back(std::move(type));
}
- return Result<Unit>({});
+ std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+ data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+ data_header->type_count_ = data->type_entries_.size();
+ data->header_ = std::move(data_header);
+ return {std::move(data)};
}
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
- AssetManager2 target_asset_manager;
- if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
- return Error("failed to create target asset manager");
- }
-
- AssetManager2 overlay_asset_manager;
- if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
- return Error("failed to create overlay asset manager");
- }
-
- const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (target_arsc == nullptr) {
- return Error("failed to load target resources.arsc");
- }
-
- const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (overlay_arsc == nullptr) {
- return Error("failed to load overlay resources.arsc");
- }
-
- const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (target_pkg == nullptr) {
- return Error("failed to load target package from resources.arsc");
- }
-
- const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (overlay_pkg == nullptr) {
- return Error("failed to load overlay package from resources.arsc");
- }
+ const std::string& target_apk_path = target_apk_assets.GetPath();
+ const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
if (!target_zip) {
@@ -366,11 +303,6 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
return Error("failed to open overlay as zip");
}
- auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
@@ -395,7 +327,7 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
- return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
+ return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
sizeof(header->target_path_));
}
memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
@@ -404,70 +336,24 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
std::unique_ptr<Idmap> idmap(new Idmap());
idmap->header_ = std::move(header);
- // find the resources that exist in both packages
- MatchingResources matching_resources;
- const auto end = overlay_pkg->end();
- for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
- const ResourceId overlay_resid = *iter;
- Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!name) {
- continue;
- }
- // prepend "<package>:" to turn name into "<package>:<type>/<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;
- }
-
- if (enforce_overlayable) {
- Result<Unit> success =
- CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
- if (!success) {
- LOG(WARNING) << "overlay \"" << overlay_apk_path
- << "\" is not allowed to overlay resource \"" << full_name
- << "\": " << success.GetErrorMessage();
- continue;
- }
- }
-
- matching_resources.Add(target_resid, overlay_resid);
+ auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+ if (!overlay_info) {
+ return overlay_info.GetError();
}
- if (matching_resources.Map().empty()) {
- return Error("overlay \"%s\" does not successfully overlay any resource",
- overlay_apk_path.c_str());
+ auto resource_mapping =
+ ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
}
- // encode idmap data
- std::unique_ptr<IdmapData> data(new IdmapData());
- const auto types_end = matching_resources.Map().cend();
- for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
- auto ei = ti->second.cbegin();
- std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
- type->target_type_id_ = EXTRACT_TYPE(ei->first);
- type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
- type->entry_offset_ = EXTRACT_ENTRY(ei->first);
- EntryId last_target_entry = kNoEntry;
- for (; ei != ti->second.cend(); ++ei) {
- if (last_target_entry != kNoEntry) {
- int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
- type->entries_.insert(type->entries_.end(), count, kNoEntry);
- }
- type->entries_.push_back(EXTRACT_ENTRY(ei->second));
- last_target_entry = EXTRACT_ENTRY(ei->first);
- }
- data->type_entries_.push_back(std::move(type));
+ auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+ if (!idmap_data) {
+ return idmap_data.GetError();
}
- std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
- data_header->target_package_id_ = target_pkg->GetPackageId();
- data_header->type_count_ = data->type_entries_.size();
- data->header_ = std::move(data_header);
-
- idmap->data_.push_back(std::move(data));
-
+ idmap->data_.push_back(std::move(*idmap_data));
return {std::move(idmap)};
}
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 000000000000..95ae626664dd
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/ResourceMapping.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::ResToTypeEntryName;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+std::string ConcatPolicies(const std::vector<std::string>& policies) {
+ std::string message;
+ for (const std::string& policy : policies) {
+ if (!message.empty()) {
+ message.append("|");
+ }
+ message.append(policy);
+ }
+
+ return message;
+}
+
+Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ const ResourceId& target_resource) {
+ static constexpr const PolicyBitmask sDefaultPolicies =
+ PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
+ PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+
+ // If the resource does not have an overlayable definition, allow the resource to be overlaid if
+ // the overlay is preinstalled or signed with the same signature as the target.
+ if (!target_package.DefinesOverlayable()) {
+ return (sDefaultPolicies & fulfilled_policies) != 0
+ ? Result<Unit>({})
+ : Error(
+ "overlay must be preinstalled or signed with the same signature as the "
+ "target");
+ }
+
+ const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
+ if (overlayable_info == nullptr) {
+ // Do not allow non-overlayable resources to be overlaid.
+ return Error("target resource has no overlayable declaration");
+ }
+
+ if (overlay_info.target_name != overlayable_info->name) {
+ // If the overlay supplies a target overlayable name, the resource must belong to the
+ // overlayable defined with the specified name to be overlaid.
+ return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
+ overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+ }
+
+ // Enforce policy restrictions if the resource is declared as overlayable.
+ if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+ return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
+ ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
+ ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+ }
+
+ return Result<Unit>({});
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+ if (packages.empty()) {
+ return nullptr;
+ }
+ int id = packages[0]->GetPackageId();
+ return loaded_arsc.GetPackageById(id);
+}
+
+Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
+ const AssetManager2& asset_manager) {
+ Res_value value{};
+ ResTable_config selected_config{};
+ uint32_t flags;
+ auto cookie =
+ asset_manager.GetResource(resource_id, /* may_be_bag */ false,
+ /* density_override */ 0U, &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return Error("failed to find resource for id 0x%08x", resource_id);
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ return Error("resource for is 0x%08x is not a file", resource_id);
+ }
+
+ auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
+ size_t len;
+ auto file_path16 = string_pool->stringAt(value.data, &len);
+ if (file_path16 == nullptr) {
+ return Error("failed to find string for index %d", value.data);
+ }
+
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto file_path = String8(String16(file_path16));
+ auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return Error("file \"%s\" not found", file_path.c_str());
+ }
+
+ return asset;
+}
+
+} // namespace
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser) {
+ ResourceMapping resource_mapping;
+ auto root_it = overlay_parser.tree_iterator();
+ if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+ return Error("root element is not <overlay> tag");
+ }
+
+ const uint8_t overlay_package_id = overlay_package->GetPackageId();
+ auto overlay_it_end = root_it.end();
+ for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+ if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("failed to parse overlay xml document");
+ }
+
+ if (overlay_it->event() != XmlParser::Event::START_TAG) {
+ continue;
+ }
+
+ if (overlay_it->name() != "item") {
+ return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+ }
+
+ Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+ if (!target_resource) {
+ return Error(R"(<item> tag missing expected attribute "target")");
+ }
+
+ Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+ if (!overlay_resource) {
+ return Error(R"(<item> tag missing expected attribute "value")");
+ }
+
+ ResourceId target_id =
+ target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
+ if (target_id == 0U) {
+ LOG(WARNING) << "failed to find resource \"" << *target_resource << "\" in target resources";
+ continue;
+ }
+
+ if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+ overlay_resource->data += string_pool_offset;
+ }
+
+ // Only rewrite resources defined within the overlay package to their corresponding target
+ // resource ids at runtime.
+ bool rewrite_overlay_reference =
+ (overlay_resource->dataType == Res_value::TYPE_REFERENCE)
+ ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+ : false;
+
+ resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
+ rewrite_overlay_reference);
+ }
+
+ return resource_mapping;
+}
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
+ const AssetManager2* target_am, const AssetManager2* overlay_am,
+ const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+ ResourceMapping resource_mapping;
+ const auto end = overlay_package->end();
+ for (auto iter = overlay_package->begin(); iter != end; ++iter) {
+ const ResourceId overlay_resid = *iter;
+ Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
+ if (!name) {
+ continue;
+ }
+
+ // Find the resource with the same type and entry name within the target package.
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
+ const ResourceId target_resource = target_am->GetResourceId(full_name);
+ if (target_resource == 0U) {
+ continue;
+ }
+
+ resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+ /* rewrite_overlay_reference */ true);
+ }
+
+ return resource_mapping;
+}
+
+void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies) {
+ std::set<ResourceId> remove_ids;
+ for (const auto& target_map : target_map_) {
+ const ResourceId target_resid = target_map.first;
+ Result<Unit> success =
+ CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
+ if (success) {
+ continue;
+ }
+
+ // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
+ // warning.
+ Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
+ if (!name) {
+ name = StringPrintf("0x%08x", target_resid);
+ }
+
+ LOG(WARNING) << "overlay \"" << overlay_package->GetPackageName()
+ << "\" is not allowed to overlay resource \"" << *name
+ << "\" in target: " << success.GetErrorMessage();
+
+ remove_ids.insert(target_resid);
+ }
+
+ for (const ResourceId target_resid : remove_ids) {
+ RemoveMapping(target_resid);
+ }
+}
+
+Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ AssetManager2 target_asset_manager;
+ if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs*/)) {
+ return Error("failed to create target asset manager");
+ }
+
+ AssetManager2 overlay_asset_manager;
+ if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs */)) {
+ return Error("failed to create overlay asset manager");
+ }
+
+ const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+ if (target_arsc == nullptr) {
+ return Error("failed to load target resources.arsc");
+ }
+
+ const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+ if (overlay_arsc == nullptr) {
+ return Error("failed to load overlay resources.arsc");
+ }
+
+ const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+ if (target_pkg == nullptr) {
+ return Error("failed to load target package from resources.arsc");
+ }
+
+ const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+ if (overlay_pkg == nullptr) {
+ return Error("failed to load overlay package from resources.arsc");
+ }
+
+ size_t string_pool_data_length = 0U;
+ size_t string_pool_offset = 0U;
+ std::unique_ptr<uint8_t[]> string_pool_data;
+ Result<ResourceMapping> resource_mapping = {{}};
+ if (overlay_info.resource_mapping != 0U) {
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+ if (!asset) {
+ return Error("failed opening xml for android:resourcesMap: %s",
+ asset.GetErrorMessage().c_str());
+ }
+
+ auto parser =
+ XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
+ if (!parser) {
+ return Error("failed opening ResXMLTree");
+ }
+
+ // Copy the xml string pool data before the parse goes out of scope.
+ auto& string_pool = (*parser)->get_strings();
+ string_pool_data_length = string_pool.bytes();
+ string_pool_data.reset(new uint8_t[string_pool_data_length]);
+ memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+ // Offset string indices by the size of the overlay resource table string pool.
+ string_pool_offset = overlay_arsc->GetStringPool()->size();
+
+ resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
+ string_pool_offset, *(*parser));
+ } else {
+ // If no file is specified using android:resourcesMap, it is assumed that the overlay only
+ // defines resources intended to override target resources of the same type and name.
+ resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
+ target_pkg, overlay_pkg);
+ }
+
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
+ }
+
+ if (enforce_overlayable) {
+ // Filter out resources the overlay is not allowed to override.
+ (*resource_mapping)
+ .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
+ fulfilled_policies);
+ }
+
+ resource_mapping->target_package_id_ = target_pkg->GetPackageId();
+ resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
+ resource_mapping->string_pool_offset_ = string_pool_offset;
+ resource_mapping->string_pool_data_ = std::move(string_pool_data);
+ resource_mapping->string_pool_data_length_ = string_pool_data_length;
+ return std::move(*resource_mapping);
+}
+
+OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
+ // An overlay resource can override multiple target resources at once. Rewrite the overlay
+ // resource as the first target resource it overrides.
+ OverlayResourceMap map;
+ for (const auto& mappings : overlay_map_) {
+ map.insert(std::make_pair(mappings.first, mappings.second));
+ }
+ return map;
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+ TargetValue::DataType data_type,
+ TargetValue::DataValue data_value,
+ bool rewrite_overlay_reference) {
+ if (target_map_.find(target_resource) != target_map_.end()) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ }
+
+ // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+ // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+ target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+
+ if (rewrite_overlay_reference && data_type == Res_value::TYPE_REFERENCE) {
+ overlay_map_.insert(std::make_pair(data_value, target_resource));
+ }
+
+ return Result<Unit>({});
+}
+
+void ResourceMapping::RemoveMapping(ResourceId target_resource) {
+ auto target_iter = target_map_.find(target_resource);
+ if (target_iter == target_map_.end()) {
+ return;
+ }
+
+ const TargetValue value = target_iter->second;
+ target_map_.erase(target_iter);
+
+ // Remove rewriting of overlay resource id to target resource id.
+ if (value.data_type != Res_value::TYPE_REFERENCE) {
+ return;
+ }
+
+ auto overlay_iter = overlay_map_.equal_range(value.data_value);
+ for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
+ if (i->second == target_resource) {
+ overlay_map_.erase(i);
+ return;
+ }
+ }
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index dce83e35978d..9d3269207c91 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,18 @@
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
-using android::idmap2::Xml;
+using android::idmap2::XmlParser;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
-Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
+Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
return Error("no resource 0x%08x in asset manager", resid);
@@ -65,52 +65,72 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
}
- std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+ Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
if (!xml) {
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
}
+ auto manifest_it = (*xml)->tree_iterator();
+ if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+ return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
+ }
+
+ auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
+ return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
+ });
+
OverlayManifestInfo info{};
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- if (assert_overlay) {
- return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
+ if (overlay_it == manifest_it.end()) {
+ if (!assert_overlay) {
+ return info;
}
- return info;
+ return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
}
- auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- if (assert_overlay) {
- return Error("android:targetPackage missing from <overlay> of %s", path.c_str());
- }
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
+ info.target_package = *result_str;
} else {
- info.target_package = iter->second;
+ return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+ result_str.GetErrorMessage().c_str());
}
- iter = tag->find("targetName");
- if (iter != tag->end()) {
- info.target_name = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
+ info.target_name = *result_str;
}
- iter = tag->find("isStatic");
- if (iter != tag->end()) {
- info.is_static = std::stoul(iter->second) != 0U;
+ if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
+ if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+ info.resource_mapping = (*result_value).data;
+ } else {
+ return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+ path.c_str());
+ }
}
- iter = tag->find("priority");
- if (iter != tag->end()) {
- info.priority = std::stoi(iter->second);
+ if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.is_static = (*result_value).data != 0U;
+ } else {
+ return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+ }
+ }
+
+ if (auto result_value = overlay_it->GetAttributeValue("priority")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.priority = (*result_value).data;
+ } else {
+ return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+ }
}
- iter = tag->find("requiredSystemPropertyName");
- if (iter != tag->end()) {
- info.requiredSystemPropertyName = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+ info.requiredSystemPropertyName = *result_str;
}
- iter = tag->find("requiredSystemPropertyValue");
- if (iter != tag->end()) {
- info.requiredSystemPropertyValue = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
+ info.requiredSystemPropertyValue = *result_str;
}
return info;
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
deleted file mode 100644
index 264586829c47..000000000000
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ /dev/null
@@ -1,80 +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.
- */
-
-#include "idmap2/Xml.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-namespace android::idmap2 {
-
-std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
- std::unique_ptr<Xml> xml(new Xml());
- if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
- return nullptr;
- }
- return xml;
-}
-
-std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
- const String16 tag_to_find(name.c_str(), name.size());
- xml_.restart();
- ResXMLParser::event_code_t type;
- do {
- type = xml_.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- const String16 tag(xml_.getElementName(&len));
- if (tag == tag_to_find) {
- std::unique_ptr<std::map<std::string, std::string>> map(
- new std::map<std::string, std::string>());
- for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
- const String16 key16(xml_.getAttributeName(i, &len));
- std::string key = String8(key16).c_str();
-
- std::string value;
- switch (xml_.getAttributeDataType(i)) {
- case Res_value::TYPE_STRING: {
- const String16 value16(xml_.getAttributeStringValue(i, &len));
- value = String8(value16).c_str();
- } break;
- case Res_value::TYPE_INT_DEC:
- case Res_value::TYPE_INT_HEX:
- case Res_value::TYPE_INT_BOOLEAN: {
- Res_value resValue;
- xml_.getAttributeValue(i, &resValue);
- value = std::to_string(resValue.data);
- } break;
- default:
- return nullptr;
- }
-
- map->emplace(std::make_pair(key, value));
- }
- return map;
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
- return nullptr;
-}
-
-Xml::~Xml() {
- xml_.uninit();
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
new file mode 100644
index 000000000000..526a560907aa
--- /dev/null
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/XmlParser.h"
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace android::idmap2 {
+
+template <typename T>
+ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
+ ResXMLParser::ResXMLPosition pos{};
+ tree.getPosition(&pos);
+ return pos;
+}
+
+XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
+}
+XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
+ : parser_(tree) {
+ set_position(pos);
+}
+
+bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
+ ResXMLParser::ResXMLPosition pos = get_position();
+ ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
+ return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
+ pos.eventCode == rhs_pos.eventCode;
+}
+
+bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
+ return !(*this == rhs);
+}
+
+ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
+ return get_tree_position(parser_);
+}
+
+void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
+ parser_.setPosition(pos);
+}
+
+bool XmlParser::Node::Seek(bool inner_child) {
+ if (parser_.getEventType() == XmlParser::Event::END_TAG) {
+ return false;
+ }
+
+ ssize_t depth = 0;
+ XmlParser::Event code;
+ while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ code != XmlParser::Event::END_DOCUMENT) {
+ if (code == XmlParser::Event::START_TAG) {
+ if (++depth == (inner_child ? 1 : 0)) {
+ return true;
+ }
+ } else if (code == XmlParser::Event::END_TAG) {
+ if (--depth == (inner_child ? -1 : -2)) {
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+XmlParser::Event XmlParser::Node::event() const {
+ return parser_.getEventType();
+}
+
+std::string XmlParser::Node::name() const {
+ size_t len;
+ const String16 key16(parser_.getElementName(&len));
+ return String8(key16).c_str();
+}
+
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+ auto value = GetAttributeValue(name);
+ if (!value) {
+ return value.GetError();
+ }
+
+ switch ((*value).dataType) {
+ case Res_value::TYPE_STRING: {
+ size_t len;
+ const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
+ return std::string(String8(value16).c_str());
+ }
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ case Res_value::TYPE_INT_BOOLEAN: {
+ return std::to_string((*value).data);
+ }
+ default:
+ return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+ }
+}
+
+Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+ size_t len;
+ for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
+ const String16 key16(parser_.getAttributeName(i, &len));
+ std::string key = String8(key16).c_str();
+ if (key != name) {
+ continue;
+ }
+
+ Res_value res_value{};
+ if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
+ return Error(R"(Bad value for attribute "%s")", name.c_str());
+ }
+
+ return res_value;
+ }
+
+ return Error(R"(Failed to find attribute "%s")", name.c_str());
+}
+
+Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
+ bool copy_data) {
+ auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
+ if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+ return Error("Malformed xml block");
+ }
+
+ // Find the beginning of the first tag.
+ XmlParser::Event event;
+ while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
+ }
+
+ if (event == XmlParser::Event::END_DOCUMENT) {
+ return Error("Root tag was not be found");
+ }
+
+ if (event == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("Bad xml document");
+ }
+
+ return parser;
+}
+
+XmlParser::~XmlParser() {
+ tree_.uninit();
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab721493..43fdc9a78186 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -75,9 +75,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e77d7b..47e5b17f4a98 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -179,8 +179,7 @@ void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_
ASSERT_THAT(overlay_apk, NotNull());
auto result =
- Idmap::FromApkAssets(target_apk_path.to_string(), *target_apk, overlay_apk_path.to_string(),
- *overlay_apk, fulfilled_policies, enforce_overlayable);
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, enforce_overlayable);
*out_idmap = result ? std::move(*result) : nullptr;
}
@@ -195,7 +194,7 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) {
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -480,9 +479,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -497,8 +495,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c41250457678..1d34e42e188d 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -43,9 +43,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 26951763cd66..c243d745e568 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -38,9 +38,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -50,7 +49,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: c054fb26 overlay crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
std::string::npos);
}
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 000000000000..1ef41de4410d
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 <cstdio> // fclose
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+namespace android::idmap2 {
+
+#define ASSERT_RESULT(r) \
+ do { \
+ auto result = r; \
+ ASSERT_TRUE(result) << result.GetErrorMessage(); \
+ } while (0)
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+ enforce_overlayable);
+}
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+ return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+ const uint8_t type, const uint32_t value, bool rewrite) {
+ auto target_map = mapping.GetTargetToOverlayMap();
+ auto entry_map = target_map.find(target_resource);
+ if (entry_map == target_map.end()) {
+ return Error("Failed to find mapping for target resource");
+ }
+
+ if (entry_map->second.data_type != type) {
+ return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+ entry_map->second.data_type);
+ }
+
+ if (entry_map->second.data_value != value) {
+ return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+ entry_map->second.data_value);
+ }
+
+ auto overlay_map = mapping.GetOverlayToTargetMap();
+ auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+ if ((overlay_iter != overlay_map.end()) != rewrite) {
+ return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
+ }
+
+ return Result<Unit>({});
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0U; // no xml
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str4
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030003; // xml/overlays_swap
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str1 -> string/str4
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str3 -> string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str4 -> string/str3
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonResourceMapping) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x0104000a,
+ false /* rewrite */)); // string/str1 -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3 -> string/str4
+}
+
+TEST(ResourceMappingTests, InlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ constexpr size_t overlay_string_pool_size = 8U;
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x03 /* Res_value::TYPE_STRING */,
+ overlay_string_pool_size + 0U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x10 /* Res_value::TYPE_INT_DEC */, 73U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+ auto resources =
+ TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
+// off.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+ true /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+ true /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+ true /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Overlays that do not target an <overlayable> tag can overlay resources defined within any
+// <overlayable> tag.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str4
+}
+
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
+// overlay packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+ auto resources =
+ TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
+
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ fulfilled_policies,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+ true /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+ true /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+ true /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+ };
+
+ CheckEntries(PolicyFlags::POLICY_SIGNATURE);
+ CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 000000000000..1a7eaca4d67b
--- /dev/null
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio> // fclose
+#include <memory>
+#include <string>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/XmlParser.h"
+#include "idmap2/ZipFile.h"
+
+namespace android::idmap2 {
+
+Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ if (zip == nullptr) {
+ return Error("Failed to open zip file");
+ }
+
+ auto data = zip->Uncompress(test_file);
+ if (data == nullptr) {
+ return Error("Failed to open xml file");
+ }
+
+ return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+}
+
+TEST(XmlParserTests, Create) {
+ auto xml = CreateTestParser("AndroidManifest.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_FALSE(fail);
+}
+
+TEST(XmlParserTests, NextChild) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ auto root_iter = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(root_iter->name(), "a");
+
+ auto a_iter = root_iter.begin();
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "b");
+
+ auto c_iter = a_iter.begin();
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(c_iter->name(), "c");
+
+ ++c_iter;
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(c_iter, a_iter.end());
+
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "d");
+
+ // Skip the <e> tag.
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(a_iter, root_iter.end());
+}
+
+TEST(XmlParserTests, AttributeValues) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter = root_iter.begin();
+ auto attribute_str = a_iter->GetAttributeStringValue("type_string");
+ ASSERT_TRUE(attribute_str);
+ ASSERT_EQ(*attribute_str, "fortytwo");
+
+ auto attribute_value = a_iter->GetAttributeValue("type_int_dec");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_hex");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_boolean");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 0xffffffff);
+}
+
+TEST(XmlParserTests, IteratorEquality) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+ auto root_iter_2 = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+ auto a_iter_2 = root_iter_2.begin();
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_EQ(a_iter_1, root_iter_1.end());
+ ASSERT_EQ(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+}
+
+TEST(XmlParserTests, Backtracking) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+
+ // Start a second iterator at the <a> tag.
+ auto root_iter_2 = root_iter_1;
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Move the first iterator to the end of the <a> tag.
+ auto root_iter_end_1 = root_iter_1.end();
+ ++root_iter_1;
+ ASSERT_NE(root_iter_1, root_iter_2);
+ ASSERT_NE(*root_iter_1, *root_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ASSERT_NE(a_iter_1, root_iter_end_1);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ASSERT_EQ(a_iter_1, root_iter_end_1);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
deleted file mode 100644
index df63211a9209..000000000000
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ /dev/null
@@ -1,68 +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.
- */
-
-#include <cstdio> // fclose
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(XmlTests, Create) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("AndroidManifest.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- fclose(stderr); // silence expected warnings from libandroidfw
- const char* not_xml = "foo";
- auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
- ASSERT_THAT(fail, IsNull());
-}
-
-TEST(XmlTests, FindTag) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("res/xml/test.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- auto attrs = xml->FindTag("c");
- ASSERT_THAT(attrs, NotNull());
- ASSERT_EQ(attrs->size(), 4U);
- ASSERT_EQ(attrs->at("type_string"), "fortytwo");
- ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
- ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
- ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
-
- auto fail = xml->FindTag("does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index 619bb6ce0f25..cf3691c3b3cf 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -16,8 +16,11 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay">
+
<application android:hasCode="false"/>
+
<overlay
android:targetPackage="test.target"
- android:targetName="TestResources"/>
+ android:targetName="TestResources"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 68b9f507a11d..b921b0d3d3ad 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
aapt2 compile --dir res -o compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
index 18ee43dc57a4..7c25985e5a61 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
index 642519008b15..c75f3e1dbddf 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
index 642ab90d00ae..5b8a6e4a90ed 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
index 2ec56020c4aa..698a1fd6e702 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 5842da4f432e..1db303ff05b5 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 000000000000..edd33f7dc90d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str1"/>
+ <item target="string/str3" value="@string/str3" />
+ <item target="string/str4" value="@string/str4" />
+ <item target="integer/int1" value="@integer/int1" />
+ <item target="integer/not_in_target" value="@integer/not_in_target" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
new file mode 100644
index 000000000000..aa7fefaa305e
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@android:string/ok"/>
+ <item target="string/str3" value="@string/str3" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
new file mode 100644
index 000000000000..e12b823ff50d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="Hello World"/>
+ <item target="integer/int1" value="73" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
new file mode 100644
index 000000000000..5728e672d94a
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str4"/>
+ <item target="string/str3" value="@string/str1" />
+ <item target="string/str4" value="@string/str3" />
+ <item target="integer/int_not_in_target" value="@integer/int1" />
+</overlay>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6b6d0a..56a3f7f0b13a 100644
--- a/cmds/idmap2/tests/data/target/res/xml/test.xml
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -14,12 +14,15 @@
limitations under the License.
-->
<a>
- <b>
- <c
- type_string="fortytwo"
- type_int_dec="42"
- type_int_hex="0x2a"
- type_int_boolean="true"
- />
+ <b type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true">
+
+ <c />
</b>
-</a>
+
+ <d>
+ <e />
+ </d>
+</a> \ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 033305aaed4f..2eb7c477c3b4 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9bcd6dcabcde..251cf46f969d 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 43e33f59f612..5a76d1f9c80d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -122,9 +122,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
- {.pullTimeoutNs = NS_PER_SEC / 2,
- .coolDownNs = NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ {.coolDownNs = NS_PER_SEC,
+ .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME),
+ .pullTimeoutNs = NS_PER_SEC / 2,
+ }},
// system_uptime
{android::util::SYSTEM_UPTIME,
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index b56c00e44d3f..fbf1f59141a8 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -22,6 +22,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager.StackInfo;
import android.content.ComponentName;
@@ -324,16 +325,17 @@ public class ActivityView extends ViewGroup {
* this method can be called.
*
* @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}.
* @param options options for the activity
*
* @see StateCallback
* @see #startActivity(Intent)
*/
- public void startActivity(@NonNull PendingIntent pendingIntent,
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options) {
options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
try {
- pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
+ pendingIntent.send(getContext(), 0 /* code */, fillInIntent,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
options.toBundle());
} catch (PendingIntent.CanceledException e) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5b211e147d64..39fab634f03c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2212,6 +2212,15 @@ class ContextImpl extends Context {
}
@Override
+ public Context createContextAsUser(UserHandle user) {
+ try {
+ return createPackageContextAsUser(getPackageName(), mFlags, user);
+ } catch (NameNotFoundException e) {
+ throw new IllegalStateException("Own package not found: package=" + getPackageName());
+ }
+ }
+
+ @Override
public Context createContextForSplit(String splitName) throws NameNotFoundException {
if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
// All Splits are always loaded.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index dda3bb53ebe3..f5914412663b 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -215,7 +215,6 @@ interface IActivityTaskManager {
void releaseSomeActivities(in IApplicationThread app);
Bitmap getTaskDescriptionIcon(in String filename, int userId);
- void startInPlaceAnimationOnFrontMostApplication(in Bundle opts);
void registerTaskStackListener(in ITaskStackListener listener);
void unregisterTaskStackListener(in ITaskStackListener listener);
void setTaskResizeable(int taskId, int resizeableMode);
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index cb9ebac728ec..9e6054c715d6 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -32,6 +32,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
+import android.content.res.loader.ResourceLoader;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.os.Process;
@@ -45,6 +46,7 @@ import android.util.Slog;
import android.view.Display;
import android.view.DisplayAdjustments;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -53,7 +55,11 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Predicate;
@@ -92,6 +98,52 @@ public class ResourcesManager {
new ArrayMap<>();
/**
+ * A list of {@link Resources} that contain unique {@link ResourcesImpl}s.
+ *
+ * These are isolated so that {@link ResourceLoader}s can be added and removed without
+ * affecting other instances.
+ *
+ * When a reference is added here, it is guaranteed that the {@link ResourcesImpl}
+ * it contains is unique to itself and will never be set to a shared reference.
+ */
+ @GuardedBy("this")
+ private List<ResourcesWithLoaders> mResourcesWithLoaders = Collections.emptyList();
+
+ private static class ResourcesWithLoaders {
+
+ private WeakReference<Resources> mResources;
+ private ResourcesKey mResourcesKey;
+
+ @Nullable
+ private WeakReference<IBinder> mActivityToken;
+
+ ResourcesWithLoaders(Resources resources, ResourcesKey resourcesKey,
+ IBinder activityToken) {
+ this.mResources = new WeakReference<>(resources);
+ this.mResourcesKey = resourcesKey;
+ this.mActivityToken = new WeakReference<>(activityToken);
+ }
+
+ @Nullable
+ Resources resources() {
+ return mResources.get();
+ }
+
+ @Nullable
+ IBinder activityToken() {
+ return mActivityToken == null ? null : mActivityToken.get();
+ }
+
+ ResourcesKey resourcesKey() {
+ return mResourcesKey;
+ }
+
+ void updateKey(ResourcesKey newKey) {
+ mResourcesKey = newKey;
+ }
+ }
+
+ /**
* A list of Resource references that can be reused.
*/
@UnsupportedAppUsage
@@ -182,15 +234,36 @@ public class ResourcesManager {
public void invalidatePath(String path) {
synchronized (this) {
int count = 0;
- for (int i = 0; i < mResourceImpls.size();) {
+
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
final ResourcesKey key = mResourceImpls.keyAt(i);
if (key.isPathReferenced(path)) {
- cleanupResourceImpl(key);
+ ResourcesImpl impl = mResourceImpls.removeAt(i).get();
+ if (impl != null) {
+ impl.flushLayoutCache();
+ }
+ count++;
+ }
+ }
+
+ for (int i = mResourcesWithLoaders.size() - 1; i >= 0; i--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(i);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ final ResourcesKey key = resourcesWithLoaders.resourcesKey();
+ if (key.isPathReferenced(path)) {
+ mResourcesWithLoaders.remove(i);
+ ResourcesImpl impl = resources.getImpl();
+ if (impl != null) {
+ impl.flushLayoutCache();
+ }
count++;
- } else {
- i++;
}
}
+
Log.i(TAG, "Invalidated " + count + " asset managers that referenced " + path);
for (int i = mCachedApkAssets.size() - 1; i >= 0; i--) {
@@ -317,15 +390,6 @@ public class ResourcesManager {
}
}
- private void cleanupResourceImpl(ResourcesKey removedKey) {
- // Remove resource key to resource impl mapping and flush cache
- final ResourcesImpl res = mResourceImpls.remove(removedKey).get();
-
- if (res != null) {
- res.flushLayoutCache();
- }
- }
-
private static String overlayPathToIdmapPath(String path) {
return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
}
@@ -499,6 +563,16 @@ public class ResourcesManager {
pw.print("resource impls: ");
pw.println(countLiveReferences(mResourceImpls.values()));
+
+ int resourcesWithLoadersCount = 0;
+ for (int index = 0; index < mResourcesWithLoaders.size(); index++) {
+ if (mResourcesWithLoaders.get(index).resources() != null) {
+ resourcesWithLoadersCount++;
+ }
+ }
+
+ pw.print("resources with loaders: ");
+ pw.println(resourcesWithLoadersCount);
}
}
@@ -579,11 +653,24 @@ public class ResourcesManager {
*/
private @Nullable ResourcesKey findKeyForResourceImplLocked(
@NonNull ResourcesImpl resourceImpl) {
- final int refCount = mResourceImpls.size();
+ int size = mResourcesWithLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ if (resourceImpl == resources.getImpl()) {
+ return resourcesWithLoaders.resourcesKey();
+ }
+ }
+
+ int refCount = mResourceImpls.size();
for (int i = 0; i < refCount; i++) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl != null && resourceImpl == impl) {
+ if (resourceImpl == impl) {
return mResourceImpls.keyAt(i);
}
}
@@ -625,31 +712,55 @@ public class ResourcesManager {
return activityResources;
}
- /**
- * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
- * or the class loader is different.
- */
- private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
- @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
- @NonNull CompatibilityInfo compatInfo) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
+ @Nullable
+ private Resources findResourcesForActivityLocked(@NonNull IBinder targetActivityToken,
+ @NonNull ResourcesKey targetKey, @NonNull ClassLoader targetClassLoader) {
+ int size = mResourcesWithLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
- final int refCount = activityResources.activityResources.size();
- for (int i = 0; i < refCount; i++) {
- WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
- Resources resources = weakResourceRef.get();
+ IBinder activityToken = resourcesWithLoaders.activityToken();
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
- if (resources != null
- && Objects.equals(resources.getClassLoader(), classLoader)
- && resources.getImpl() == impl) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing ref=" + resources);
- }
+ ClassLoader classLoader = resources.getClassLoader();
+
+ if (Objects.equals(activityToken, targetActivityToken)
+ && Objects.equals(key, targetKey)
+ && Objects.equals(classLoader, targetClassLoader)) {
+ return resources;
+ }
+ }
+
+ ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ targetActivityToken);
+
+ size = activityResources.activityResources.size();
+ for (int index = 0; index < size; index++) {
+ WeakReference<Resources> ref = activityResources.activityResources.get(index);
+ Resources resources = ref.get();
+ ResourcesKey key = resources == null ? null : findKeyForResourceImplLocked(
+ resources.getImpl());
+
+ if (key != null
+ && Objects.equals(resources.getClassLoader(), targetClassLoader)
+ && Objects.equals(key, targetKey)) {
return resources;
}
}
+ return null;
+ }
+
+ private @NonNull Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+ @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
+ @NonNull CompatibilityInfo compatInfo) {
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
@@ -661,28 +772,8 @@ public class ResourcesManager {
return resources;
}
- /**
- * Gets an existing Resources object if the class loader and ResourcesImpl are the same,
- * otherwise creates a new Resources object.
- */
- private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
+ private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
- // Find an existing Resources that has this ResourcesImpl set.
- final int refCount = mResourceReferences.size();
- for (int i = 0; i < refCount; i++) {
- WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
- Resources resources = weakResourceRef.get();
- if (resources != null &&
- Objects.equals(resources.getClassLoader(), classLoader) &&
- resources.getImpl() == impl) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing ref=" + resources);
- }
- return resources;
- }
- }
-
- // Create a new Resources reference and use the existing ResourcesImpl object.
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
@@ -750,16 +841,70 @@ public class ResourcesManager {
updateResourcesForActivity(activityToken, overrideConfig, displayId,
false /* movedToDifferentDisplay */);
+ cleanupReferences(activityToken);
+ rebaseKeyForActivity(activityToken, key);
+
+ synchronized (this) {
+ Resources resources = findResourcesForActivityLocked(activityToken, key,
+ classLoader);
+ if (resources != null) {
+ return resources;
+ }
+ }
+
// Now request an actual Resources object.
- return getOrCreateResources(activityToken, key, classLoader);
+ return createResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
- * Gets an existing Resources object set with a ResourcesImpl object matching the given key,
- * or creates one if it doesn't exist.
+ * Rebases a key's override config on top of the Activity's base override.
+ */
+ private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key) {
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(activityToken);
+
+ // Clean up any dead references so they don't pile up.
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+
+ // Rebase the key's override config on top of the Activity's base override.
+ if (key.hasOverrideConfiguration()
+ && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
+ final Configuration temp = new Configuration(activityResources.overrideConfig);
+ temp.updateFrom(key.mOverrideConfiguration);
+ key.mOverrideConfiguration.setTo(temp);
+ }
+ }
+
+ /**
+ * Check WeakReferences and remove any dead references so they don't pile up.
+ * @param activityToken optional token to clean up Activity resources
+ */
+ private void cleanupReferences(IBinder activityToken) {
+ if (activityToken != null) {
+ ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
+ if (activityResources != null) {
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+ }
+ } else {
+ ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
+ }
+
+ for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ mResourcesWithLoaders.remove(index);
+ }
+ }
+ }
+
+ /**
+ * Creates a Resources object set with a ResourcesImpl object matching the given key.
*
* @param activityToken The Activity this Resources object should be associated with.
* @param key The key describing the parameters of the ResourcesImpl object.
@@ -769,7 +914,7 @@ public class ResourcesManager {
* {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
* is called.
*/
- private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
+ private @Nullable Resources createResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
synchronized (this) {
if (DEBUG) {
@@ -778,66 +923,17 @@ public class ResourcesManager {
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
}
- if (activityToken != null) {
- final ActivityResources activityResources =
- getOrCreateActivityResourcesStructLocked(activityToken);
-
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(activityResources.activityResources,
- sEmptyReferencePredicate);
-
- // Rebase the key's override config on top of the Activity's base override.
- if (key.hasOverrideConfiguration()
- && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
- final Configuration temp = new Configuration(activityResources.overrideConfig);
- temp.updateFrom(key.mOverrideConfiguration);
- key.mOverrideConfiguration.setTo(temp);
- }
-
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl, key.mCompatInfo);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
-
- } else {
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
-
- // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
- }
-
- // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
+ ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
if (resourcesImpl == null) {
return null;
}
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
-
- final Resources resources;
if (activityToken != null) {
- resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ return createResourcesForActivityLocked(activityToken, classLoader,
resourcesImpl, key.mCompatInfo);
} else {
- resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
+ return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
- return resources;
}
}
@@ -868,7 +964,8 @@ public class ResourcesManager {
* {@link ClassLoader#getSystemClassLoader()} is used.
* @return a Resources object from which to access resources.
*/
- public @Nullable Resources getResources(@Nullable IBinder activityToken,
+ public @Nullable Resources getResources(
+ @Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -888,7 +985,14 @@ public class ResourcesManager {
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo);
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- return getOrCreateResources(activityToken, key, classLoader);
+
+ cleanupReferences(activityToken);
+
+ if (activityToken != null) {
+ rebaseKeyForActivity(activityToken, key);
+ }
+
+ return createResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -944,67 +1048,40 @@ public class ResourcesManager {
here);
}
- final boolean activityHasOverrideConfig =
- !activityResources.overrideConfig.equals(Configuration.EMPTY);
// Rebase each Resources associated with this Activity.
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
WeakReference<Resources> weakResRef = activityResources.activityResources.get(
i);
+
Resources resources = weakResRef.get();
if (resources == null) {
continue;
}
- // Extract the ResourcesKey that was last used to create the Resources for this
- // activity.
- final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
- if (oldKey == null) {
- Slog.e(TAG, "can't find ResourcesKey for resources impl="
- + resources.getImpl());
- continue;
- }
-
- // Build the new override configuration for this ResourcesKey.
- final Configuration rebasedOverrideConfig = new Configuration();
- if (overrideConfig != null) {
- rebasedOverrideConfig.setTo(overrideConfig);
- }
+ ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+ overrideConfig, displayId);
+ updateActivityResources(resources, newKey, false);
+ }
- if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
- // Generate a delta between the old base Activity override configuration and
- // the actual final override configuration that was used to figure out the
- // real delta this Resources object wanted.
- Configuration overrideOverrideConfig = Configuration.generateDelta(
- oldConfig, oldKey.mOverrideConfiguration);
- rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ // Also find loaders that are associated with an Activity
+ final int loaderCount = mResourcesWithLoaders.size();
+ for (int index = loaderCount - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(
+ index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null
+ || resourcesWithLoaders.activityToken() != activityToken) {
+ continue;
}
- // Create the new ResourcesKey with the rebased override config.
- final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
- oldKey.mSplitResDirs,
- oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
- rebasedOverrideConfig, oldKey.mCompatInfo);
-
- if (DEBUG) {
- Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
- + " to newKey=" + newKey + ", displayId=" + displayId);
- }
+ ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+ overrideConfig, displayId);
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
- if (resourcesImpl == null) {
- resourcesImpl = createResourcesImpl(newKey);
- if (resourcesImpl != null) {
- mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
- }
- }
+ updateActivityResources(resources, newKey, true);
- if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
- // Set the ResourcesImpl, updating it for all users of this Resources
- // object.
- resources.setImpl(resourcesImpl);
- }
+ resourcesWithLoaders.updateKey(newKey);
}
}
} finally {
@@ -1012,6 +1089,70 @@ public class ResourcesManager {
}
}
+ /**
+ * Rebases an updated override config over any old override config and returns the new one
+ * that an Activity's Resources should be set to.
+ */
+ private ResourcesKey rebaseActivityOverrideConfig(Resources resources,
+ Configuration oldOverrideConfig, @Nullable Configuration newOverrideConfig,
+ int displayId) {
+ // Extract the ResourcesKey that was last used to create the Resources for this
+ // activity.
+ final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+ if (oldKey == null) {
+ Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ + resources.getImpl());
+ return null;
+ }
+
+ // Build the new override configuration for this ResourcesKey.
+ final Configuration rebasedOverrideConfig = new Configuration();
+ if (newOverrideConfig != null) {
+ rebasedOverrideConfig.setTo(newOverrideConfig);
+ }
+
+ final boolean hadOverrideConfig = !oldOverrideConfig.equals(Configuration.EMPTY);
+ if (hadOverrideConfig && oldKey.hasOverrideConfiguration()) {
+ // Generate a delta between the old base Activity override configuration and
+ // the actual final override configuration that was used to figure out the
+ // real delta this Resources object wanted.
+ Configuration overrideOverrideConfig = Configuration.generateDelta(
+ oldOverrideConfig, oldKey.mOverrideConfiguration);
+ rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ }
+
+ // Create the new ResourcesKey with the rebased override config.
+ final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+ oldKey.mSplitResDirs,
+ oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
+ rebasedOverrideConfig, oldKey.mCompatInfo);
+
+ if (DEBUG) {
+ Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ + " to newKey=" + newKey + ", displayId=" + displayId);
+ }
+
+ return newKey;
+ }
+
+ private void updateActivityResources(Resources resources, ResourcesKey newKey,
+ boolean hasLoader) {
+ final ResourcesImpl resourcesImpl;
+
+ if (hasLoader) {
+ // Loaders always get new Impls because they cannot be shared
+ resourcesImpl = createResourcesImpl(newKey);
+ } else {
+ resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey);
+ }
+
+ if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+ // Set the ResourcesImpl, updating it for all users of this Resources
+ // object.
+ resources.setImpl(resourcesImpl);
+ }
+ }
+
@TestApi
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
@@ -1050,61 +1191,77 @@ public class ResourcesManager {
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
- Configuration tmpConfig = null;
+ Configuration tmpConfig = new Configuration();
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + config);
- int displayId = key.mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
- final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfiguration) {
- if (tmpConfig == null) {
- tmpConfig = new Configuration();
- }
- tmpConfig.setTo(config);
-
- // Get new DisplayMetrics based on the DisplayAdjustments given
- // to the ResourcesImpl. Update a copy if the CompatibilityInfo
- // changed, because the ResourcesImpl object will handle the
- // update internally.
- DisplayAdjustments daj = r.getDisplayAdjustments();
- if (compat != null) {
- daj = new DisplayAdjustments(daj);
- daj.setCompatibilityInfo(compat);
- }
- dm = getDisplayMetrics(displayId, daj);
-
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
-
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- r.updateConfiguration(tmpConfig, dm, compat);
- } else {
- r.updateConfiguration(config, dm, compat);
- }
- //Slog.i(TAG, "Updated app resources " + v.getKey()
- // + " " + r + ": " + r.getConfiguration());
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig,
+ defaultDisplayMetrics, key, r);
} else {
- //Slog.i(TAG, "Removing old resources " + v.getKey());
mResourceImpls.removeAt(i);
}
}
+ for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ mResourcesWithLoaders.remove(index);
+ continue;
+ }
+
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig,
+ defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(),
+ resources.getImpl());
+ }
+
return changes != 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
+ private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat, Configuration tmpConfig,
+ DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) {
+ if (DEBUG || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Changing resources "
+ + resourcesImpl + " config to: " + config);
+ }
+ int displayId = key.mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+ if (!isDefaultDisplay || hasOverrideConfiguration) {
+ tmpConfig.setTo(config);
+
+ // Get new DisplayMetrics based on the DisplayAdjustments given
+ // to the ResourcesImpl. Update a copy if the CompatibilityInfo
+ // changed, because the ResourcesImpl object will handle the
+ // update internally.
+ DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
+ if (compat != null) {
+ daj = new DisplayAdjustments(daj);
+ daj.setCompatibilityInfo(compat);
+ }
+ dm = getDisplayMetrics(displayId, daj);
+
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
+
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
+ }
+ resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
+ } else {
+ resourcesImpl.updateConfiguration(config, dm, compat);
+ }
+ }
+
/**
* Appends the library asset path to any ResourcesImpl object that contains the main
* assetPath.
@@ -1140,7 +1297,7 @@ public class ResourcesManager {
ArrayUtils.appendElement(String.class, newLibAssets, libAsset);
}
- if (newLibAssets != key.mLibDirs) {
+ if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
@@ -1153,10 +1310,106 @@ public class ResourcesManager {
}
}
+ final int count = mResourcesWithLoaders.size();
+ for (int index = 0; index < count; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
+ if (Objects.equals(key.mResDir, assetPath)) {
+ String[] newLibAssets = key.mLibDirs;
+ for (String libAsset : libAssets) {
+ newLibAssets =
+ ArrayUtils.appendElement(String.class, newLibAssets, libAsset);
+ }
+
+ if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
+ updatedResourceKeys.put(resources.getImpl(),
+ new ResourcesKey(
+ key.mResDir,
+ key.mSplitResDirs,
+ key.mOverlayDirs,
+ newLibAssets,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo));
+ }
+ }
+ }
+
redirectResourcesToNewImplLocked(updatedResourceKeys);
}
}
+ /**
+ * Mark a {@link Resources} as containing a {@link ResourceLoader}.
+ *
+ * This removes its {@link ResourcesImpl} from the shared pool and creates it a new one. It is
+ * then tracked separately, kept unique, and restored properly across {@link Activity}
+ * recreations.
+ */
+ public void registerForLoaders(Resources resources) {
+ synchronized (this) {
+ boolean found = false;
+ IBinder activityToken = null;
+
+ // Remove the Resources from the reference list as it's now tracked separately
+ int size = mResourceReferences.size();
+ for (int index = 0; index < size; index++) {
+ WeakReference<Resources> reference = mResourceReferences.get(index);
+ if (reference.get() == resources) {
+ mResourceReferences.remove(index);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // Do the same removal for any Activity Resources
+ for (Map.Entry<IBinder, ActivityResources> entry :
+ mActivityResourceReferences.entrySet()) {
+ ArrayList<WeakReference<Resources>> activityResourcesList =
+ entry.getValue().activityResources;
+ final int resCount = activityResourcesList.size();
+ for (int index = 0; index < resCount; index++) {
+ WeakReference<Resources> reference = activityResourcesList.get(index);
+ if (reference.get() == resources) {
+ activityToken = entry.getKey();
+ activityResourcesList.remove(index);
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ throw new IllegalArgumentException("Resources " + resources
+ + " registered for loaders but was not previously tracked by"
+ + " ResourcesManager");
+ }
+
+ ResourcesKey key = findKeyForResourceImplLocked(resources.getImpl());
+ ResourcesImpl impl = createResourcesImpl(key);
+
+ if (mResourcesWithLoaders == Collections.EMPTY_LIST) {
+ mResourcesWithLoaders = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ mResourcesWithLoaders.add(new ResourcesWithLoaders(resources, key, activityToken));
+
+ // Set the new Impl, which is now guaranteed to be unique per Resources object
+ resources.setImpl(impl);
+ }
+ }
+
// TODO(adamlesinski): Make this accept more than just overlay directories.
final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
@Nullable final String[] oldPaths) {
@@ -1201,6 +1454,32 @@ public class ResourcesManager {
}
}
+ final int count = mResourcesWithLoaders.size();
+ for (int index = 0; index < count; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
+ updatedResourceKeys.put(resources.getImpl(),
+ new ResourcesKey(
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo
+ ));
+ }
+ }
+
redirectResourcesToNewImplLocked(updatedResourceKeys);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
@@ -1250,5 +1529,25 @@ public class ResourcesManager {
}
}
}
+
+ // Update any references that need to be re-built with loaders. These are intentionally not
+ // part of either of the above lists.
+ final int loaderCount = mResourcesWithLoaders.size();
+ for (int index = loaderCount - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey newKey = updatedResourceKeys.get(resources.getImpl());
+ if (newKey == null) {
+ continue;
+ }
+
+ resourcesWithLoaders.updateKey(newKey);
+ resourcesWithLoaders.resources()
+ .setImpl(createResourcesImpl(newKey));
+ }
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 08817e05e0a5..227684b1bd14 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1559,7 +1559,14 @@ public abstract class Context {
* @see Environment#getExternalStorageState(File)
* @see Environment#isExternalStorageEmulated(File)
* @see Environment#isExternalStorageRemovable(File)
+ * @deprecated These directories still exist and are scanned, but developers
+ * are encouraged to migrate to inserting content into a
+ * {@link MediaStore} collection directly, as any app can
+ * contribute new media to {@link MediaStore} with no
+ * permissions required, starting in
+ * {@link android.os.Build.VERSION_CODES#Q}.
*/
+ @Deprecated
public abstract File[] getExternalMediaDirs();
/**
@@ -5217,8 +5224,9 @@ public abstract class Context {
*/
@SystemApi
@TestApi
+ @NonNull
public Context createPackageContextAsUser(
- String packageName, @CreatePackageOptions int flags, UserHandle user)
+ @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
if (Build.IS_ENG) {
throw new IllegalStateException("createPackageContextAsUser not overridden!");
@@ -5227,6 +5235,23 @@ public abstract class Context {
}
/**
+ * Similar to {@link #createPackageContext(String, int)}, but for the own package with a
+ * different {@link UserHandle}. For example, {@link #getContentResolver()}
+ * will open any {@link Uri} as the given user.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @NonNull
+ public Context createContextAsUser(@NonNull UserHandle user) {
+ if (Build.IS_ENG) {
+ throw new IllegalStateException("createContextAsUser not overridden!");
+ }
+ return this;
+ }
+
+ /**
* Creates a context given an {@link android.content.pm.ApplicationInfo}.
*
* @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 0859f97e81a1..f7cd51e7ffbc 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -885,6 +885,12 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
+ public Context createContextAsUser(UserHandle user) {
+ return mBase.createContextAsUser(user);
+ }
+
+ /** @hide */
+ @Override
@UnsupportedAppUsage
public Context createApplicationContext(ApplicationInfo application,
int flags) throws PackageManager.NameNotFoundException {
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 853e8189ea8a..a2f8886eb7d2 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -166,9 +166,8 @@ public class OverlayManager {
}
/**
- * Returns information about all overlays for the given target package for
- * the specified user. The returned list is ordered according to the
- * overlay priority with the highest priority at the end of the list.
+ * Clear part of the overlay manager's internal cache of PackageInfo
+ * objects. Only intended for testing.
*
* @param targetPackageName The name of the target package.
* @param user The user to get the OverlayInfos for.
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index a35ad567ed81..de1d514d0a5b 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -16,7 +16,10 @@
package android.content.res;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.content.res.loader.ResourcesProvider;
+import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -36,10 +39,14 @@ import java.io.IOException;
*/
public final class ApkAssets {
@GuardedBy("this") private final long mNativePtr;
+
+ @Nullable
@GuardedBy("this") private final StringBlock mStringBlock;
@GuardedBy("this") private boolean mOpen = true;
+ private final boolean mForLoader;
+
/**
* Creates a new ApkAssets instance from the given path on disk.
*
@@ -48,7 +55,8 @@ public final class ApkAssets {
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
- return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -61,7 +69,8 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
throws IOException {
- return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
+ return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -76,7 +85,8 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
boolean forceSharedLibrary) throws IOException {
- return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
+ return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -96,7 +106,8 @@ public final class ApkAssets {
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
throws IOException {
- return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
+ return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
+ false /*forLoader*/);
}
/**
@@ -110,21 +121,90 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
throws IOException {
- return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
+ return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk for use with a
+ * {@link ResourcesProvider}.
+ *
+ * @param path The path to an APK on disk.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadApkForLoader(@NonNull String path)
+ throws IOException {
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLibrary*/,
+ false /*overlay*/, false /*arscOnly*/, true /*forLoader*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor for use with a
+ * {@link ResourcesProvider}.
+ *
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ *
+ * @param fd The FileDescriptor of an open, readable APK.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ @NonNull
+ public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
+ return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
+ false /*system*/, false /*forceSharedLib*/, false /*arscOnly*/, true /*forLoader*/);
}
- private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor representing an ARSC
+ * for use with a {@link ResourcesProvider}.
+ *
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ *
+ * @param fd The FileDescriptor of an open, readable .arsc.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadArscForLoader(@NonNull FileDescriptor fd)
throws IOException {
+ return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
+ false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
+ }
+
+ /**
+ * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
+ * is required for a lot of APIs, and it's easier to have a non-null reference rather than
+ * tracking a separate identifier.
+ */
+ @NonNull
+ public static ApkAssets loadEmptyForLoader() {
+ return new ApkAssets(true);
+ }
+
+ private ApkAssets(boolean forLoader) {
+ mForLoader = forLoader;
+ mNativePtr = nativeLoadEmpty(forLoader);
+ mStringBlock = null;
+ }
+
+ private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
+ boolean arscOnly, boolean forLoader) throws IOException {
+ mForLoader = forLoader;
Preconditions.checkNotNull(path, "path");
- mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
+ mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
+ : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
- boolean forceSharedLib) throws IOException {
+ boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
+ mForLoader = forLoader;
Preconditions.checkNotNull(fd, "fd");
Preconditions.checkNotNull(friendlyName, "friendlyName");
- mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
+ mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
+ : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
@@ -136,11 +216,19 @@ public final class ApkAssets {
}
CharSequence getStringFromPool(int idx) {
+ if (mStringBlock == null) {
+ return null;
+ }
+
synchronized (this) {
return mStringBlock.get(idx);
}
}
+ public boolean isForLoader() {
+ return mForLoader;
+ }
+
/**
* Retrieve a parser for a compiled XML file. This is associated with a single APK and
* <em>NOT</em> a full AssetManager. This means that shared-library references will not be
@@ -192,18 +280,26 @@ public final class ApkAssets {
synchronized (this) {
if (mOpen) {
mOpen = false;
- mStringBlock.close();
+ if (mStringBlock != null) {
+ mStringBlock.close();
+ }
nativeDestroy(mNativePtr);
}
}
}
- private static native long nativeLoad(
- @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ private static native long nativeLoad(@NonNull String path, boolean system,
+ boolean forceSharedLib, boolean overlay, boolean forLoader)
throws IOException;
private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, boolean system, boolean forceSharedLib)
+ @NonNull String friendlyName, boolean system, boolean forceSharedLib,
+ boolean forLoader)
+ throws IOException;
+ private static native long nativeLoadArsc(@NonNull String path, boolean forLoader)
throws IOException;
+ private static native long nativeLoadArscFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, boolean forLoader) throws IOException;
+ private static native long nativeLoadEmpty(boolean forLoader);
private static native void nativeDestroy(long ptr);
private static native @NonNull String nativeGetAssetPath(long ptr);
private static native long nativeGetStringBlock(long ptr);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 7d6dc97e82cb..23e772075ad6 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -27,9 +27,13 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
+import android.content.res.loader.ResourceLoader;
+import android.content.res.loader.ResourceLoaderManager;
+import android.content.res.loader.ResourcesProvider;
import android.os.ParcelFileDescriptor;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -39,15 +43,19 @@ import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
import java.io.BufferedReader;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.FileLock;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -110,6 +118,13 @@ public final class AssetManager implements AutoCloseable {
@GuardedBy("this") private int mNumRefs = 1;
@GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+ private ResourceLoaderManager mResourceLoaderManager;
+
+ /** @hide */
+ public void setResourceLoaderManager(ResourceLoaderManager resourceLoaderManager) {
+ mResourceLoaderManager = resourceLoaderManager;
+ }
+
/**
* A Builder class that helps create an AssetManager with only a single invocation of
* {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
@@ -507,7 +522,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ outValue.string = getPooledStringForCookie(cookie, outValue.data);
}
return true;
}
@@ -554,7 +569,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ return getPooledStringForCookie(cookie, outValue.data);
}
return outValue.coerceToString();
}
@@ -632,7 +647,7 @@ public final class AssetManager implements AutoCloseable {
int cookie = rawInfoArray[i];
int index = rawInfoArray[i + 1];
retArray[j] = (index >= 0 && cookie > 0)
- ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
+ ? getPooledStringForCookie(cookie, index) : null;
}
return retArray;
}
@@ -688,7 +703,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ outValue.string = getPooledStringForCookie(cookie, outValue.data);
}
return true;
}
@@ -753,6 +768,7 @@ public final class AssetManager implements AutoCloseable {
*
* @hide
*/
+ @TestApi
public void setResourceResolutionLoggingEnabled(boolean enabled) {
synchronized (this) {
ensureValidLocked();
@@ -768,6 +784,7 @@ public final class AssetManager implements AutoCloseable {
*
* @hide
*/
+ @TestApi
public @Nullable String getLastResourceResolution() {
synchronized (this) {
ensureValidLocked();
@@ -814,6 +831,13 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ String path = Paths.get("assets", fileName).toString();
+ InputStream inputStream = searchLoaders(0, path, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+
final long asset = nativeOpenAsset(mObject, fileName, accessMode);
if (asset == 0) {
throw new FileNotFoundException("Asset file: " + fileName);
@@ -838,6 +862,13 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ String path = Paths.get("assets", fileName).toString();
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(0, path);
+ if (fileDescriptor != null) {
+ return fileDescriptor;
+ }
+
final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
if (pfd == null) {
throw new FileNotFoundException("Asset file: " + fileName);
@@ -931,6 +962,12 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ InputStream inputStream = searchLoaders(cookie, fileName, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+
final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
if (asset == 0) {
throw new FileNotFoundException("Asset absolute file: " + fileName);
@@ -970,6 +1007,12 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName);
+ if (fileDescriptor != null) {
+ return fileDescriptor;
+ }
+
final ParcelFileDescriptor pfd =
nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
if (pfd == null) {
@@ -1031,7 +1074,16 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
- final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+
+ final long xmlBlock;
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName);
+ if (fileDescriptor != null) {
+ xmlBlock = nativeOpenXmlAssetFd(mObject, cookie,
+ fileDescriptor.getFileDescriptor());
+ } else {
+ xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ }
+
if (xmlBlock == 0) {
throw new FileNotFoundException("Asset XML file: " + fileName);
}
@@ -1041,6 +1093,85 @@ public final class AssetManager implements AutoCloseable {
}
}
+ private InputStream searchLoaders(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ if (mResourceLoaderManager == null) {
+ return null;
+ }
+
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders =
+ mResourceLoaderManager.getInternalList();
+
+ // A cookie of 0 means no specific ApkAssets, so search everything
+ if (cookie == 0) {
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ try {
+ InputStream inputStream = pair.first.loadAsset(fileName, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+ } catch (IOException ignored) {
+ // When searching, ignore read failures
+ }
+ }
+
+ return null;
+ }
+
+ ApkAssets apkAssets = mApkAssets[cookie - 1];
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ return pair.first.loadAsset(fileName, accessMode);
+ }
+ }
+
+ return null;
+ }
+
+ private AssetFileDescriptor searchLoadersFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ if (mResourceLoaderManager == null) {
+ return null;
+ }
+
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders =
+ mResourceLoaderManager.getInternalList();
+
+ // A cookie of 0 means no specific ApkAssets, so search everything
+ if (cookie == 0) {
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ try {
+ ParcelFileDescriptor fileDescriptor = pair.first.loadAssetFd(fileName);
+ if (fileDescriptor != null) {
+ return new AssetFileDescriptor(fileDescriptor, 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ } catch (IOException ignored) {
+ // When searching, ignore read failures
+ }
+ }
+
+ return null;
+ }
+
+ ApkAssets apkAssets = mApkAssets[cookie - 1];
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ ParcelFileDescriptor fileDescriptor = pair.first.loadAssetFd(fileName);
+ if (fileDescriptor != null) {
+ return new AssetFileDescriptor(fileDescriptor, 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ return null;
+ }
+ }
+ return null;
+ }
+
void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
@@ -1296,7 +1427,7 @@ public final class AssetManager implements AutoCloseable {
*
* <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
* <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
- * parsed using {@link java.util.Locale#forLanguageTag(String)}.
+ * parsed using {@link Locale#forLanguageTag(String)}.
*
* <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
@@ -1439,6 +1570,8 @@ public final class AssetManager implements AutoCloseable {
private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
@NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+ private static native long nativeOpenXmlAssetFd(long ptr, int cookie,
+ @NonNull FileDescriptor fileDescriptor);
// Primitive resource native methods.
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index d7e4e1452cfe..2698c2de4c61 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -30,6 +30,7 @@ import android.annotation.DimenRes;
import android.annotation.DrawableRes;
import android.annotation.FontRes;
import android.annotation.FractionRes;
+import android.annotation.IntRange;
import android.annotation.IntegerRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
@@ -41,8 +42,12 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.annotation.UnsupportedAppUsage;
import android.annotation.XmlRes;
+import android.app.ResourcesManager;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
+import android.content.res.loader.ResourceLoader;
+import android.content.res.loader.ResourceLoaderManager;
+import android.content.res.loader.ResourcesProvider;
import android.graphics.Movie;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -54,13 +59,16 @@ import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import android.util.TypedValue;
import android.view.DisplayAdjustments;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -71,6 +79,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -133,6 +143,11 @@ public class Resources {
@UnsupportedAppUsage
final ClassLoader mClassLoader;
+ private final Object mResourceLoaderLock = new Object();
+
+ @GuardedBy("mResourceLoaderLock")
+ private ResourceLoaderManager mResourceLoaderManager;
+
/**
* WeakReferences to Themes that were constructed from this Resources object.
* We keep track of these in case our underlying implementation is changed, in which case
@@ -148,6 +163,8 @@ public class Resources {
private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
+ private int mBaseApkAssetsSize;
+
/**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
@@ -283,8 +300,15 @@ public class Resources {
return;
}
+ mBaseApkAssetsSize = ArrayUtils.size(impl.getAssets().getApkAssets());
mResourcesImpl = impl;
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager != null) {
+ mResourceLoaderManager.onImplUpdate(mResourcesImpl);
+ }
+ }
+
// Create new ThemeImpls that are identical to the ones we have.
synchronized (mThemeRefs) {
final int count = mThemeRefs.size();
@@ -903,7 +927,7 @@ public class Resources {
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValueForDensity(id, density, value, true);
- return impl.loadDrawable(this, value, id, density, theme);
+ return loadDrawable(value, id, density, theme);
} finally {
releaseTempTypedValue(value);
}
@@ -913,6 +937,14 @@ public class Resources {
@UnsupportedAppUsage
Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
throws NotFoundException {
+ ResourceLoader loader = findLoader(value.assetCookie);
+ if (loader != null) {
+ Drawable drawable = loader.loadDrawable(value, id, density, theme);
+ if (drawable != null) {
+ return drawable;
+ }
+ }
+
return mResourcesImpl.loadDrawable(this, value, id, density, theme);
}
@@ -2280,7 +2312,7 @@ public class Resources {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
- return impl.loadXmlResourceParser(value.string.toString(), id,
+ return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
@@ -2304,6 +2336,14 @@ public class Resources {
@UnsupportedAppUsage
XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
String type) throws NotFoundException {
+ ResourceLoader loader = findLoader(assetCookie);
+ if (loader != null) {
+ XmlResourceParser xml = loader.loadXmlResourceParser(file, id);
+ if (xml != null) {
+ return xml;
+ }
+ }
+
return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
}
@@ -2329,4 +2369,137 @@ public class Resources {
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
+
+ private ResourceLoader findLoader(int assetCookie) {
+ ApkAssets[] apkAssetsArray = mResourcesImpl.getAssets().getApkAssets();
+ int apkAssetsIndex = assetCookie - 1;
+ if (apkAssetsIndex < apkAssetsArray.length && apkAssetsIndex >= 0) {
+ ApkAssets apkAssets = apkAssetsArray[apkAssetsIndex];
+ if (apkAssets.isForLoader()) {
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders;
+ // Since we don't lock the entire resolution path anyways,
+ // only lock here instead of entire method. The list is copied
+ // and effectively a snapshot is used.
+ synchronized (mResourceLoaderLock) {
+ loaders = mResourceLoaderManager.getInternalList();
+ }
+
+ if (!ArrayUtils.isEmpty(loaders)) {
+ int size = loaders.size();
+ for (int index = 0; index < size; index++) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ return pair.first;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return copied list of loaders and providers previously added
+ */
+ @NonNull
+ public List<Pair<ResourceLoader, ResourcesProvider>> getLoaders() {
+ synchronized (mResourceLoaderLock) {
+ return mResourceLoaderManager == null
+ ? Collections.emptyList()
+ : mResourceLoaderManager.getLoaders();
+ }
+ }
+
+ /**
+ * Add a custom {@link ResourceLoader} which is added to the paths searched by
+ * {@link AssetManager} when resolving a resource.
+ *
+ * Resources are resolved as if the loader was a resource overlay, meaning the latest
+ * in the list, of equal or better config, is returned.
+ *
+ * {@link ResourcesProvider}s passed in here are not managed and a reference should be held
+ * to remove, re-use, or close them when necessary.
+ *
+ * @param resourceLoader an interface used to resolve file paths for drawables/XML files;
+ * a reference should be kept to remove the loader if necessary
+ * @param resourcesProvider an .apk or .arsc file representation
+ * @param index where to add the loader in the list
+ * @throws IllegalArgumentException if the resourceLoader is already added
+ * @throws IndexOutOfBoundsException if the index is invalid
+ */
+ public void addLoader(@NonNull ResourceLoader resourceLoader,
+ @NonNull ResourcesProvider resourcesProvider, @IntRange(from = 0) int index) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ ResourcesManager.getInstance().registerForLoaders(this);
+ mResourceLoaderManager = new ResourceLoaderManager(mResourcesImpl);
+ }
+
+ mResourceLoaderManager.addLoader(resourceLoader, resourcesProvider, index);
+ }
+ }
+
+ /**
+ * @see #addLoader(ResourceLoader, ResourcesProvider, int).
+ *
+ * Adds to the end of the list.
+ *
+ * @return index the loader was added at
+ */
+ public int addLoader(@NonNull ResourceLoader resourceLoader,
+ @NonNull ResourcesProvider resourcesProvider) {
+ synchronized (mResourceLoaderLock) {
+ int index = getLoaders().size();
+ addLoader(resourceLoader, resourcesProvider, index);
+ return index;
+ }
+ }
+
+ /**
+ * Remove a loader previously added by
+ * {@link #addLoader(ResourceLoader, ResourcesProvider, int)}
+ *
+ * The caller maintains responsibility for holding a reference to the matching
+ * {@link ResourcesProvider} and closing it after this method has been called.
+ *
+ * @param resourceLoader the same reference passed into [addLoader
+ * @return the index the loader was at in the list, or -1 if the loader was not found
+ */
+ public int removeLoader(@NonNull ResourceLoader resourceLoader) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ return -1;
+ }
+
+ return mResourceLoaderManager.removeLoader(resourceLoader);
+ }
+ }
+
+ /**
+ * Swap the current set of loaders. Preferred to multiple remove/add calls as this doesn't
+ * update the resource data structures after each modification.
+ *
+ * Set to null or an empty list to clear the set of loaders.
+ *
+ * The caller maintains responsibility for holding references to the added
+ * {@link ResourcesProvider}s and closing them after this method has been called.
+ *
+ * @param resourceLoadersAndProviders a list of pairs to add
+ */
+ public void setLoaders(
+ @Nullable List<Pair<ResourceLoader, ResourcesProvider>> resourceLoadersAndProviders) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ if (ArrayUtils.isEmpty(resourceLoadersAndProviders)) {
+ return;
+ }
+
+ ResourcesManager.getInstance().registerForLoaders(this);
+ mResourceLoaderManager = new ResourceLoaderManager(mResourcesImpl);
+ }
+
+ mResourceLoaderManager.setLoaders(resourceLoadersAndProviders);
+ }
+ }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b72544c02d6a..84489cfb768c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -57,6 +57,8 @@ import android.view.DisplayAdjustments;
import com.android.internal.util.GrowingArrayUtils;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -376,7 +378,7 @@ public class ResourcesImpl {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
try {
synchronized (mAccessLock) {
- if (false) {
+ if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+ mConfiguration + " old compat is "
+ mDisplayAdjustments.getCompatibilityInfo());
@@ -572,6 +574,20 @@ public class ResourcesImpl {
}
}
+ /**
+ * Wipe all caches that might be read and return an outdated object when resolving a resource.
+ */
+ public void clearAllCaches() {
+ synchronized (mAccessLock) {
+ mDrawableCache.clear();
+ mColorDrawableCache.clear();
+ mComplexColorCache.clear();
+ mAnimatorCache.clear();
+ mStateListAnimatorCache.clear();
+ flushLayoutCache();
+ }
+ }
+
@Nullable
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
@@ -802,6 +818,27 @@ public class ResourcesImpl {
}
/**
+ * Loads a Drawable from an encoded image stream, or null.
+ *
+ * This call will handle closing the {@link InputStream}.
+ */
+ @Nullable
+ private Drawable decodeImageDrawable(@NonNull InputStream inputStream,
+ @NonNull Resources wrapper, @NonNull TypedValue value) {
+ ImageDecoder.Source src = ImageDecoder.createSource(wrapper, inputStream, value.density);
+ try {
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) ->
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE));
+ } catch (IOException ignored) {
+ // This is okay. This may be something that ImageDecoder does not
+ // support, like SVG.
+ return null;
+ } finally {
+ IoUtils.closeQuietly(inputStream);
+ }
+ }
+
+ /**
* Loads a drawable from XML or resources stream.
*
* @return Drawable, or null if Drawable cannot be decoded.
@@ -865,8 +902,12 @@ public class ResourcesImpl {
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- AssetInputStream ais = (AssetInputStream) is;
- dr = decodeImageDrawable(ais, wrapper, value);
+ if (is instanceof AssetInputStream) {
+ AssetInputStream ais = (AssetInputStream) is;
+ dr = decodeImageDrawable(ais, wrapper, value);
+ } else {
+ dr = decodeImageDrawable(is, wrapper, value);
+ }
}
} finally {
stack.pop();
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 2ae1932c3437..d43bd36b4c74 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -47,6 +47,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import java.io.Closeable;
import java.util.Arrays;
/**
@@ -54,7 +55,7 @@ import java.util.Arrays;
*
* {@hide}
*/
-final class StringBlock {
+public final class StringBlock implements Closeable {
private static final String TAG = "AssetManager";
private static final boolean localLOGV = false;
@@ -175,6 +176,7 @@ final class StringBlock {
}
}
+ @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -517,7 +519,7 @@ final class StringBlock {
* of this newly creating StringBlock.
*/
@UnsupportedAppUsage
- StringBlock(long obj, boolean useSparse) {
+ public StringBlock(long obj, boolean useSparse) {
mNative = obj;
mUseSparse = useSparse;
mOwnsNative = false;
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index 06cafdb2bb91..968ab401ccba 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -22,8 +22,8 @@ import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
import android.content.res.Resources.ThemeKey;
-import android.util.LongSparseArray;
import android.util.ArrayMap;
+import android.util.LongSparseArray;
import java.lang.ref.WeakReference;
@@ -234,4 +234,18 @@ abstract class ThemedResourceCache<T> {
return entry == null || (configChanges != 0
&& shouldInvalidateEntry(entry, configChanges));
}
+
+ public synchronized void clear() {
+ if (mThemedEntries != null) {
+ mThemedEntries.clear();
+ }
+
+ if (mUnthemedEntries != null) {
+ mUnthemedEntries.clear();
+ }
+
+ if (mNullThemedEntries != null) {
+ mNullThemedEntries.clear();
+ }
+ }
}
diff --git a/core/java/android/content/res/loader/DirectoryResourceLoader.java b/core/java/android/content/res/loader/DirectoryResourceLoader.java
new file mode 100644
index 000000000000..7d90e72ab07e
--- /dev/null
+++ b/core/java/android/content/res/loader/DirectoryResourceLoader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A {@link ResourceLoader} that searches a directory for assets.
+ *
+ * Assumes that resource paths are resolvable child paths of the directory passed in.
+ */
+public class DirectoryResourceLoader implements ResourceLoader {
+
+ @NonNull
+ private final File mDirectory;
+
+ public DirectoryResourceLoader(@NonNull File directory) {
+ this.mDirectory = directory;
+ }
+
+ @Nullable
+ @Override
+ public InputStream loadAsset(@NonNull String path, int accessMode) throws IOException {
+ File file = findFile(path);
+ if (file == null || !file.exists()) {
+ return null;
+ }
+ return new FileInputStream(file);
+ }
+
+ @Nullable
+ @Override
+ public ParcelFileDescriptor loadAssetFd(@NonNull String path) throws IOException {
+ File file = findFile(path);
+ if (file == null || !file.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ /**
+ * Find the file for the given path encoded into the resource table.
+ */
+ @Nullable
+ public File findFile(@NonNull String path) {
+ return mDirectory.toPath().resolve(path).toFile();
+ }
+
+ @NonNull
+ public File getDirectory() {
+ return mDirectory;
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourceLoader.java b/core/java/android/content/res/loader/ResourceLoader.java
new file mode 100644
index 000000000000..af32aa2c6875
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourceLoader.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.AnyRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.ParcelFileDescriptor;
+import android.util.TypedValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Exposes methods for overriding file-based resource loading from a {@link Resources}.
+ *
+ * To be used with {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)} and related
+ * methods to override resource loading.
+ *
+ * Note that this class doesn't actually contain any resource data. Non-file-based resources are
+ * loaded directly from the {@link ResourcesProvider}'s .arsc representation.
+ *
+ * An instance's methods will only be called if its corresponding {@link ResourcesProvider}'s
+ * resources table contains an entry for the resource ID being resolved,
+ * with the exception of the non-cookie variants of {@link AssetManager}'s openAsset and
+ * openNonAsset.
+ *
+ * Those methods search backwards through all {@link ResourceLoader}s and then any paths provided
+ * by the application or system.
+ *
+ * Otherwise, an ARSC that defines R.drawable.some_id must be provided if a {@link ResourceLoader}
+ * wants to point R.drawable.some_id to a different file on disk.
+ */
+public interface ResourceLoader {
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return a
+ * {@link Drawable} which should be returned by the parent
+ * {@link Resources#getDrawable(int, Resources.Theme)}.
+ *
+ * @param value the resolved {@link TypedValue} before it has been converted to a Drawable
+ * object
+ * @param id the R.drawable ID this resolution is for
+ * @param density the requested density
+ * @param theme the {@link Resources.Theme} resolved under
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider},
+ * including calling through to {@link #loadAsset(String, int)} or {@link #loadAssetFd(String)}
+ */
+ @Nullable
+ default Drawable loadDrawable(@NonNull TypedValue value, int id, int density,
+ @Nullable Resources.Theme theme) {
+ return null;
+ }
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return an
+ * {@link XmlResourceParser} which should be returned by the parent
+ * {@link Resources#getDrawable(int, Resources.Theme)}.
+ *
+ * @param path the string that was found in the string pool
+ * @param id the XML ID this resolution is for, can be R.anim, R.layout, or R.xml
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider},
+ * including calling through to {@link #loadAssetFd(String)} (String, int)}
+ */
+ @Nullable
+ default XmlResourceParser loadXmlResourceParser(@NonNull String path, @AnyRes int id) {
+ return null;
+ }
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return an
+ * {@link InputStream} which should be returned when an asset is loaded by {@link AssetManager}.
+ * Assets will be loaded from a provider's root, with anything in its assets subpath prefixed
+ * with "assets/".
+ *
+ * @param path the asset path to load
+ * @param accessMode {@link AssetManager} access mode; does not have to be respected
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider}
+ */
+ @Nullable
+ default InputStream loadAsset(@NonNull String path, int accessMode) throws IOException {
+ return null;
+ }
+
+ /**
+ * {@link ParcelFileDescriptor} variant of {@link #loadAsset(String, int)}.
+ *
+ * @param path the asset path to load
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider}
+ */
+ @Nullable
+ default ParcelFileDescriptor loadAssetFd(@NonNull String path) throws IOException {
+ return null;
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourceLoaderManager.java b/core/java/android/content/res/loader/ResourceLoaderManager.java
new file mode 100644
index 000000000000..ddbfa81390e4
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourceLoaderManager.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.Nullable;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.ResourcesImpl;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class ResourceLoaderManager {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<Pair<ResourceLoader, ResourcesProvider>> mResourceLoaders =
+ new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private ResourcesImpl mResourcesImpl;
+
+ public ResourceLoaderManager(ResourcesImpl resourcesImpl) {
+ this.mResourcesImpl = resourcesImpl;
+ this.mResourcesImpl.getAssets().setResourceLoaderManager(this);
+ }
+
+ /**
+ * Copies the list to ensure that ongoing mutations don't affect the list if it's being used
+ * as a search set.
+ *
+ * @see Resources#getLoaders()
+ */
+ public List<Pair<ResourceLoader, ResourcesProvider>> getLoaders() {
+ synchronized (mLock) {
+ return new ArrayList<>(mResourceLoaders);
+ }
+ }
+
+ /**
+ * Returns a list for searching for a loader. Locks and copies the list to ensure that
+ * ongoing mutations don't affect the search set.
+ */
+ public List<Pair<ResourceLoader, ResourcesProvider>> getInternalList() {
+ synchronized (mLock) {
+ return new ArrayList<>(mResourceLoaders);
+ }
+ }
+
+ /**
+ * TODO(b/136251855): Consider optional boolean ignoreConfigurations to allow ResourceLoader
+ * to override every configuration in the target package
+ *
+ * @see Resources#addLoader(ResourceLoader, ResourcesProvider)
+ */
+ public void addLoader(ResourceLoader resourceLoader, ResourcesProvider resourcesProvider,
+ int index) {
+ synchronized (mLock) {
+ for (int listIndex = 0; listIndex < mResourceLoaders.size(); listIndex++) {
+ if (Objects.equals(mResourceLoaders.get(listIndex).first, resourceLoader)) {
+ throw new IllegalArgumentException("Cannot add the same ResourceLoader twice");
+ }
+ }
+
+ mResourceLoaders.add(index, Pair.create(resourceLoader, resourcesProvider));
+ updateLoaders();
+ }
+ }
+
+ /**
+ * @see Resources#removeLoader(ResourceLoader)
+ */
+ public int removeLoader(ResourceLoader resourceLoader) {
+ synchronized (mLock) {
+ int indexOfLoader = -1;
+
+ for (int index = 0; index < mResourceLoaders.size(); index++) {
+ if (mResourceLoaders.get(index).first == resourceLoader) {
+ indexOfLoader = index;
+ break;
+ }
+ }
+
+ if (indexOfLoader < 0) {
+ return indexOfLoader;
+ }
+
+ mResourceLoaders.remove(indexOfLoader);
+ updateLoaders();
+ return indexOfLoader;
+ }
+ }
+
+ /**
+ * @see Resources#setLoaders(List)
+ */
+ public void setLoaders(
+ @Nullable List<Pair<ResourceLoader, ResourcesProvider>> newLoadersAndProviders) {
+ synchronized (mLock) {
+ if (ArrayUtils.isEmpty(newLoadersAndProviders)) {
+ mResourceLoaders.clear();
+ updateLoaders();
+ return;
+ }
+
+ int size = newLoadersAndProviders.size();
+ for (int newIndex = 0; newIndex < size; newIndex++) {
+ ResourceLoader resourceLoader = newLoadersAndProviders.get(newIndex).first;
+ for (int oldIndex = 0; oldIndex < mResourceLoaders.size(); oldIndex++) {
+ if (Objects.equals(mResourceLoaders.get(oldIndex).first, resourceLoader)) {
+ throw new IllegalArgumentException(
+ "Cannot add the same ResourceLoader twice");
+ }
+ }
+ }
+
+ mResourceLoaders.clear();
+ mResourceLoaders.addAll(newLoadersAndProviders);
+
+ updateLoaders();
+ }
+ }
+
+ /**
+ * Swap the tracked {@link ResourcesImpl} and reattach any loaders to it.
+ */
+ public void onImplUpdate(ResourcesImpl resourcesImpl) {
+ synchronized (mLock) {
+ this.mResourcesImpl = resourcesImpl;
+ updateLoaders();
+ }
+ }
+
+ private void updateLoaders() {
+ synchronized (mLock) {
+ AssetManager assetManager = mResourcesImpl.getAssets();
+ ApkAssets[] existingApkAssets = assetManager.getApkAssets();
+ int baseApkAssetsSize = 0;
+ for (int index = existingApkAssets.length - 1; index >= 0; index--) {
+ // Loaders are always last, so the first non-loader is the end of the base assets
+ if (!existingApkAssets[index].isForLoader()) {
+ baseApkAssetsSize = index + 1;
+ break;
+ }
+ }
+
+ List<ApkAssets> newAssets = new ArrayList<>();
+ for (int index = 0; index < baseApkAssetsSize; index++) {
+ newAssets.add(existingApkAssets[index]);
+ }
+
+ int size = mResourceLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ApkAssets apkAssets = mResourceLoaders.get(index).second.getApkAssets();
+ newAssets.add(apkAssets);
+ }
+
+ assetManager.setApkAssets(newAssets.toArray(new ApkAssets[0]), true);
+
+ // Short of resolving every resource, it's too difficult to determine what has changed
+ // when a resource loader is changed, so just clear everything.
+ mResourcesImpl.clearAllCaches();
+ }
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
new file mode 100644
index 000000000000..050aeb7c5fda
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.ApkAssets;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Provides methods to load resources from an .apk or .arsc file to pass to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}.
+ *
+ * It is the responsibility of the app to close any instances.
+ */
+public final class ResourcesProvider implements AutoCloseable, Closeable {
+
+ /**
+ * Contains no data, assuming that any resource loading behavior will be handled in the
+ * corresponding {@link ResourceLoader}.
+ */
+ @NonNull
+ public static ResourcesProvider empty() {
+ return new ResourcesProvider(ApkAssets.loadEmptyForLoader());
+ }
+
+ /**
+ * Read from an .apk file descriptor.
+ *
+ * The file descriptor is duplicated and the one passed in may be closed by the application
+ * at any time.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadApkForLoader(fileDescriptor.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .apk file representation in memory.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromApk(@NonNull SharedMemory sharedMemory)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadApkForLoader(sharedMemory.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .arsc file descriptor.
+ *
+ * The file descriptor is duplicated and the one passed in may be closed by the application
+ * at any time.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromArsc(@NonNull ParcelFileDescriptor fileDescriptor)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadArscForLoader(fileDescriptor.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .arsc file representation in memory.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromArsc(@NonNull SharedMemory sharedMemory)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadArscForLoader(sharedMemory.getFileDescriptor()));
+ }
+
+ /**
+ * Read from a split installed alongside the application, which may not have been
+ * loaded initially because the application requested isolated split loading.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromSplit(@NonNull Context context,
+ @NonNull String splitName) throws IOException {
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ int splitIndex = ArrayUtils.indexOf(appInfo.splitNames, splitName);
+ if (splitIndex < 0) {
+ throw new IllegalArgumentException("Split " + splitName + " not found");
+ }
+
+ String splitPath = appInfo.getSplitCodePaths()[splitIndex];
+ return new ResourcesProvider(ApkAssets.loadApkForLoader(splitPath));
+ }
+
+
+ @NonNull
+ private final ApkAssets mApkAssets;
+
+ private ResourcesProvider(@NonNull ApkAssets apkAssets) {
+ this.mApkAssets = apkAssets;
+ }
+
+ /** @hide */
+ @NonNull
+ public ApkAssets getApkAssets() {
+ return mApkAssets;
+ }
+
+ @Override
+ public void close() {
+ try {
+ mApkAssets.close();
+ } catch (Throwable ignored) {
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index bcb94ce2d2d5..fdb44e7050e1 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -35,12 +35,15 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.Log;
+import android.util.Size;
import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
@@ -204,6 +207,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Create a new ParcelFileDescriptor accessing a given file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with files hosted outside your app, use an API like
+ * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
*
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
@@ -226,6 +233,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Create a new ParcelFileDescriptor accessing a given file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with files hosted outside your app, use an API like
+ * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
*
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 079a42ddaa6c..493f9a2ce123 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -47,6 +47,7 @@ import android.graphics.Point;
import android.graphics.PostProcessor;
import android.media.ExifInterface;
import android.media.MediaFile;
+import android.media.MediaFormat;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -3007,22 +3008,32 @@ public final class MediaStore {
public static final String BOOKMARK = "bookmark";
/**
- * The standard of color aspects
- * @hide
+ * The color standard of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_STANDARD_BT709
+ * @see MediaFormat#COLOR_STANDARD_BT601_PAL
+ * @see MediaFormat#COLOR_STANDARD_BT601_NTSC
+ * @see MediaFormat#COLOR_STANDARD_BT2020
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_STANDARD = "color_standard";
/**
- * The transfer of color aspects
- * @hide
+ * The color transfer of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_TRANSFER_LINEAR
+ * @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
+ * @see MediaFormat#COLOR_TRANSFER_ST2084
+ * @see MediaFormat#COLOR_TRANSFER_HLG
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_TRANSFER = "color_transfer";
/**
- * The range of color aspects
- * @hide
+ * The color range of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_RANGE_LIMITED
+ * @see MediaFormat#COLOR_RANGE_FULL
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_RANGE = "color_range";
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 10a9aaaa5b0c..08e4eeb45400 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -38,10 +38,8 @@ public final class InputWindowHandle {
// The input application handle.
public final InputApplicationHandle inputApplicationHandle;
- // The client window.
- public final IWindow clientWindow;
-
- // The token associated with the window.
+ // The token associates input data with a window and its input channel. The client input
+ // channel and the server input channel will both contain this token.
public IBinder token;
// The window name.
@@ -120,10 +118,8 @@ public final class InputWindowHandle {
private native void nativeDispose();
- public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
- IWindow clientWindow, int displayId) {
+ public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
this.inputApplicationHandle = inputApplicationHandle;
- this.clientWindow = clientWindow;
this.displayId = displayId;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6637c5b06a1b..8dd475e0c306 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -199,7 +199,10 @@ public final class SurfaceControl implements Parcelable {
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
- long mNativeObject; // package visibility only for Surface.java access
+ /**
+ * @hide
+ */
+ public long mNativeObject;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -318,6 +321,11 @@ public final class SurfaceControl implements Parcelable {
public static final int FX_SURFACE_CONTAINER = 0x00080000;
/**
+ * @hide
+ */
+ public static final int FX_SURFACE_BLAST = 0x00040000;
+
+ /**
* Mask used for FX values above.
*
* @hide
@@ -693,6 +701,14 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @hide
+ */
+ public Builder setBLASTLayer() {
+ unsetBufferSize();
+ return setFlags(FX_SURFACE_BLAST, FX_SURFACE_MASK);
+ }
+
+ /**
* Indicates whether a 'ContainerLayer' is to be constructed.
*
* Container layers will not be rendered in any fashion and instead are used
@@ -1951,7 +1967,10 @@ public final class SurfaceControl implements Parcelable {
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
Transaction.class.getClassLoader(),
nativeGetNativeTransactionFinalizer(), 512);
- private long mNativeObject;
+ /**
+ * @hide
+ */
+ public long mNativeObject;
private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
Runnable mFreeNativeResources;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c5d0a9b0a335..1599afb358e0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -75,6 +75,7 @@ import android.graphics.RenderNode;
import android.graphics.Shader;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManagerGlobal;
import android.net.Uri;
import android.os.Build;
@@ -5229,6 +5230,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
+ GradientDrawable.sWrapNegativeAngleMeasurements =
+ targetSdkVersion >= Build.VERSION_CODES.Q;
sCompatibilityDone = true;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cf0698575851..9ddd84f1943c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -45,6 +45,7 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.FrameInfo;
@@ -170,6 +171,8 @@ public final class ViewRootImpl implements ViewParent,
*/
private static final boolean MT_RENDERER_AVAILABLE = true;
+ private static final boolean USE_BLAST_BUFFERQUEUE = false;
+
/**
* If set to 2, the view system will switch from using rectangles retrieved from window to
* dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
@@ -475,6 +478,9 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
+ private SurfaceControl mBlastSurfaceControl;
+
+ private BLASTBufferQueue mBlastBufferQueue;
/**
* Transaction object that can be used to synchronize child SurfaceControl changes with
@@ -1282,6 +1288,11 @@ public final class ViewRootImpl implements ViewParent,
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
+ if (USE_BLAST_BUFFERQUEUE) {
+ mWindowAttributes.privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+ }
+
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
@@ -1629,6 +1640,29 @@ public final class ViewRootImpl implements ViewParent,
return mBoundsLayer;
}
+ Surface getOrCreateBLASTSurface(int width, int height) {
+ if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+ return null;
+ }
+ if (mBlastSurfaceControl == null) {
+ mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mSurfaceControl)
+ .setName("BLAST")
+ .setBLASTLayer()
+ .build();
+ mBlastBufferQueue = new BLASTBufferQueue(
+ mBlastSurfaceControl, width, height);
+
+ }
+ mBlastBufferQueue.update(mSurfaceControl, width, height);
+
+ mTransaction.show(mBlastSurfaceControl)
+ .reparent(mBlastSurfaceControl, mSurfaceControl)
+ .apply();
+
+ return mBlastBufferQueue.getSurface();
+ }
+
private void setBoundsLayerCrop() {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
@@ -1658,6 +1692,13 @@ public final class ViewRootImpl implements ViewParent,
}
mSurface.release();
mSurfaceControl.release();
+
+ if (mBlastBufferQueue != null) {
+ mTransaction.remove(mBlastSurfaceControl).apply();
+ mBlastSurfaceControl = null;
+ // We should probably add an explicit dispose.
+ mBlastBufferQueue = null;
+ }
}
/**
@@ -2413,10 +2454,9 @@ public final class ViewRootImpl implements ViewParent,
// will be transparent
if (mAttachInfo.mThreadedRenderer != null) {
try {
- hwInitialized = mAttachInfo.mThreadedRenderer.initialize(
- mSurface);
+ hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
if (hwInitialized && (host.mPrivateFlags
- & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
+ & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
// Don't pre-allocate if transparent regions
// are requested as they may not be needed
mAttachInfo.mThreadedRenderer.allocateBuffers();
@@ -7139,7 +7179,13 @@ public final class ViewRootImpl implements ViewParent,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
if (mSurfaceControl.isValid()) {
- mSurface.copyFrom(mSurfaceControl);
+ if (USE_BLAST_BUFFERQUEUE == false) {
+ mSurface.copyFrom(mSurfaceControl);
+ } else {
+ mSurface.transferFrom(getOrCreateBLASTSurface(
+ (int) (mView.getMeasuredWidth() * appScale + 0.5f),
+ (int) (mView.getMeasuredHeight() * appScale + 0.5f)));
+ }
} else {
destroySurface();
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 001ab6650551..0b42cd93eb17 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -197,12 +197,6 @@ public interface WindowManager extends ViewManager {
int TRANSIT_TASK_OPEN_BEHIND = 16;
/**
- * A window in a task is being animated in-place.
- * @hide
- */
- int TRANSIT_TASK_IN_PLACE = 17;
-
- /**
* An activity is being relaunched (e.g. due to configuration change).
* @hide
*/
@@ -286,7 +280,6 @@ public interface WindowManager extends ViewManager {
TRANSIT_WALLPAPER_INTRA_OPEN,
TRANSIT_WALLPAPER_INTRA_CLOSE,
TRANSIT_TASK_OPEN_BEHIND,
- TRANSIT_TASK_IN_PLACE,
TRANSIT_ACTIVITY_RELAUNCH,
TRANSIT_DOCK_TASK_FROM_RECENTS,
TRANSIT_KEYGUARD_GOING_AWAY,
@@ -1834,6 +1827,13 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 0x01000000;
/**
+ * Flag to request creation of a BLAST (Buffer as LayerState) Layer.
+ * If not specified the client will receive a BufferQueue layer.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_USE_BLAST = 0x02000000;
+
+ /**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
* @hide
diff --git a/core/java/android/view/inspector/OWNERS b/core/java/android/view/inspector/OWNERS
index 4554fdc2bfa0..c2827cc31592 100644
--- a/core/java/android/view/inspector/OWNERS
+++ b/core/java/android/view/inspector/OWNERS
@@ -1,3 +1,3 @@
alanv@google.com
aurimas@google.com
-emberr@google.com
+emberrose@google.com
diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java
index 9183227b3962..b146e3f614a2 100644
--- a/core/java/android/webkit/FindAddress.java
+++ b/core/java/android/webkit/FindAddress.java
@@ -154,7 +154,7 @@ class FindAddress {
// A house number component is "one" or a number, optionally
// followed by a single alphabetic character, or
- private static final String HOUSE_COMPONENT = "(?:one|\\d+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
+ private static final String HOUSE_COMPONENT = "(?:one|[0-9]+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
// House numbers are a repetition of |HOUSE_COMPONENT|, separated by -, and followed by
// a delimiter character.
@@ -253,10 +253,10 @@ class FindAddress {
Pattern.CASE_INSENSITIVE);
private static final Pattern sSuffixedNumberRe =
- Pattern.compile("(\\d+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("([0-9]+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
private static final Pattern sZipCodeRe =
- Pattern.compile("(?:\\d{5}(?:-\\d{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
+ Pattern.compile("(?:[0-9]{5}(?:-[0-9]{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
private static boolean checkHouseNumber(String houseNumber) {
// Make sure that there are at most 5 digits.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 407a85f1bb05..068056f091d7 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -388,21 +388,24 @@ public class ResolverActivity extends Activity {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
mSystemWindowInsets.right, 0);
- View emptyView = findViewById(R.id.empty);
- if (emptyView != null) {
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
- }
-
- if (mFooterSpacer == null) {
- mFooterSpacer = new Space(getApplicationContext());
+ // Need extra padding so the list can fully scroll up
+ if (useLayoutWithDefault()) {
+ if (mFooterSpacer == null) {
+ mFooterSpacer = new Space(getApplicationContext());
+ } else {
+ ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ }
+ mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+ mSystemWindowInsets.bottom));
+ ((ListView) mAdapterView).addFooterView(mFooterSpacer);
} else {
- ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ View emptyView = findViewById(R.id.empty);
+ if (emptyView != null) {
+ emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelSize(
+ R.dimen.chooser_edge_margin_normal) * 2);
+ }
}
- mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
- mSystemWindowInsets.bottom));
- ((ListView) mAdapterView).addFooterView(mFooterSpacer);
resetButtonBar();
@@ -561,7 +564,7 @@ public class ResolverActivity extends Activity {
intent.getData().getHost(),
mAdapter.getFilteredItem().getDisplayLabel());
} else if (mAdapter.areAllTargetsBrowsers()) {
- dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
+ dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
} else {
dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
intent.getData().getHost());
@@ -1304,6 +1307,7 @@ public class ResolverActivity extends Activity {
// In case this method is called again (due to activity recreation), avoid adding a new
// header if one is already present.
if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+ listView.setHeaderDividersEnabled(true);
listView.addHeaderView(LayoutInflater.from(this).inflate(
R.layout.resolver_different_item_header, listView, false));
}
@@ -1346,11 +1350,13 @@ public class ResolverActivity extends Activity {
final ViewGroup buttonLayout = findViewById(R.id.button_bar);
if (buttonLayout != null) {
buttonLayout.setVisibility(View.VISIBLE);
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
@@ -2057,7 +2063,9 @@ public class ResolverActivity extends Activity {
CharSequence subLabel = info.getExtendedInfo();
if (TextUtils.equals(label, subLabel)) subLabel = null;
- if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+ if (!TextUtils.equals(holder.text2.getText(), subLabel)
+ && !TextUtils.isEmpty(subLabel)) {
+ holder.text2.setVisibility(View.VISIBLE);
holder.text2.setText(subLabel);
}
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 2df515835026..0ed252400aa1 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -16,7 +16,6 @@
package com.android.internal.app.procstats;
-
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.SystemClock;
@@ -24,23 +23,37 @@ import android.os.UserHandle;
import android.service.procstats.PackageAssociationProcessStatsProto;
import android.service.procstats.PackageAssociationSourceProcessStatsProto;
import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Objects;
public final class AssociationState {
private static final String TAG = "ProcessStats";
private static final boolean DEBUG = false;
+ private static final boolean VALIDATE_TIMES = false;
+
private final ProcessStats mProcessStats;
private final ProcessStats.PackageState mPackageState;
private final String mProcessName;
private final String mName;
+ private int mTotalNesting;
+ private long mTotalStartUptime;
+ private int mTotalCount;
+ private long mTotalDuration;
+ private int mTotalActiveNesting;
+ private long mTotalActiveStartUptime;
+ private int mTotalActiveCount;
+ private long mTotalActiveDuration;
+
public final class SourceState {
final SourceKey mKey;
int mProcStateSeq = -1;
@@ -55,7 +68,7 @@ public final class AssociationState {
int mActiveProcState = ProcessStats.STATE_NOTHING;
long mActiveStartUptime;
long mActiveDuration;
- DurationsTable mDurations;
+ DurationsTable mActiveDurations;
SourceState(SourceKey key) {
mKey = key;
@@ -97,9 +110,9 @@ public final class AssociationState {
public void stop() {
mNesting--;
if (mNesting == 0) {
- mDuration += SystemClock.uptimeMillis() - mStartUptime;
- mNumActive--;
- stopTracking(SystemClock.uptimeMillis());
+ final long now = SystemClock.uptimeMillis();
+ mDuration += now - mStartUptime;
+ stopTracking(now);
}
}
@@ -108,20 +121,25 @@ public final class AssociationState {
if (mActiveStartUptime == 0) {
mActiveStartUptime = now;
mActiveCount++;
+ AssociationState.this.mTotalActiveNesting++;
+ if (AssociationState.this.mTotalActiveNesting == 1) {
+ AssociationState.this.mTotalActiveCount++;
+ AssociationState.this.mTotalActiveStartUptime = now;
+ }
}
if (mActiveProcState != mProcState) {
if (mActiveProcState != ProcessStats.STATE_NOTHING) {
// Currently active proc state changed, need to store the duration
// so far and switch tracking to the new proc state.
- final long duration = mActiveDuration + now - mActiveStartUptime;
- if (duration != 0) {
- if (mDurations == null) {
+ final long addedDuration = mActiveDuration + now - mActiveStartUptime;
+ mActiveStartUptime = now;
+ if (addedDuration != 0) {
+ if (mActiveDurations == null) {
makeDurations();
}
- mDurations.addDuration(mActiveProcState, duration);
+ mActiveDurations.addDuration(mActiveProcState, addedDuration);
mActiveDuration = 0;
}
- mActiveStartUptime = now;
}
mActiveProcState = mProcState;
}
@@ -135,21 +153,44 @@ public final class AssociationState {
if (!mInTrackingList) {
Slog.wtf(TAG, "stopActive while not tracking: " + this);
}
- final long duration = mActiveDuration + now - mActiveStartUptime;
- if (mDurations != null) {
- mDurations.addDuration(mActiveProcState, duration);
+ final long addedDuration = now - mActiveStartUptime;
+ mActiveStartUptime = 0;
+ if (mActiveDurations != null) {
+ mActiveDurations.addDuration(mActiveProcState, addedDuration);
} else {
- mActiveDuration = duration;
+ mActiveDuration += addedDuration;
+ }
+ AssociationState.this.mTotalActiveNesting--;
+ if (AssociationState.this.mTotalActiveNesting == 0) {
+ AssociationState.this.mTotalActiveDuration += now
+ - AssociationState.this.mTotalActiveStartUptime;
+ AssociationState.this.mTotalActiveStartUptime = 0;
+ if (VALIDATE_TIMES) {
+ if (mActiveDuration > AssociationState.this.mTotalActiveDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source act duration " + mActiveDurations
+ + " exceeds total " + AssociationState.this.mTotalActiveDuration
+ + " in procstate " + mActiveProcState + " in source "
+ + mKey.mProcess + " to assoc "
+ + AssociationState.this.mName, ex);
+ }
+
+ }
}
- mActiveStartUptime = 0;
}
}
void makeDurations() {
- mDurations = new DurationsTable(mProcessStats.mTableData);
+ mActiveDurations = new DurationsTable(mProcessStats.mTableData);
}
void stopTracking(long now) {
+ AssociationState.this.mTotalNesting--;
+ if (AssociationState.this.mTotalNesting == 0) {
+ AssociationState.this.mTotalDuration += now
+ - AssociationState.this.mTotalStartUptime;
+ }
stopActive(now);
if (mInTrackingList) {
mInTrackingList = false;
@@ -181,7 +222,17 @@ public final class AssociationState {
}
}
- private final static class SourceKey {
+ public final class SourceDumpContainer {
+ public final SourceState mState;
+ public long mTotalTime;
+ public long mActiveTime;
+
+ public SourceDumpContainer(SourceState state) {
+ mState = state;
+ }
+ }
+
+ public static final class SourceKey {
/**
* UID, consider this final. Not final just to avoid a temporary object during lookup.
*/
@@ -239,12 +290,10 @@ public final class AssociationState {
*/
private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
- private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
+ private static final SourceKey sTmpSourceKey = new SourceKey(0, null, null);
private ProcessState mProc;
- private int mNumActive;
-
public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
String name, String processName, ProcessState proc) {
mProcessStats = processStats;
@@ -278,11 +327,24 @@ public final class AssociationState {
mProc = proc;
}
+ public long getTotalDuration(long now) {
+ return mTotalDuration
+ + (mTotalNesting > 0 ? (now - mTotalStartUptime) : 0);
+ }
+
+ public long getActiveDuration(long now) {
+ return mTotalActiveDuration
+ + (mTotalActiveNesting > 0 ? (now - mTotalActiveStartUptime) : 0);
+ }
+
public SourceState startSource(int uid, String processName, String packageName) {
- mTmpSourceKey.mUid = uid;
- mTmpSourceKey.mProcess = processName;
- mTmpSourceKey.mPackage = packageName;
- SourceState src = mSources.get(mTmpSourceKey);
+ SourceState src;
+ synchronized (sTmpSourceKey) {
+ sTmpSourceKey.mUid = uid;
+ sTmpSourceKey.mProcess = processName;
+ sTmpSourceKey.mPackage = packageName;
+ src = mSources.get(sTmpSourceKey);
+ }
if (src == null) {
SourceKey key = new SourceKey(uid, processName, packageName);
src = new SourceState(key);
@@ -290,43 +352,88 @@ public final class AssociationState {
}
src.mNesting++;
if (src.mNesting == 1) {
+ final long now = SystemClock.uptimeMillis();
src.mCount++;
- src.mStartUptime = SystemClock.uptimeMillis();
- mNumActive++;
+ src.mStartUptime = now;
+ mTotalNesting++;
+ if (mTotalNesting == 1) {
+ mTotalCount++;
+ mTotalStartUptime = now;
+ }
}
return src;
}
public void add(AssociationState other) {
+ mTotalCount += other.mTotalCount;
+ final long origDuration = mTotalDuration;
+ mTotalDuration += other.mTotalDuration;
+ mTotalActiveCount += other.mTotalActiveCount;
+ mTotalActiveDuration += other.mTotalActiveDuration;
for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
final SourceKey key = other.mSources.keyAt(isrc);
final SourceState otherSrc = other.mSources.valueAt(isrc);
SourceState mySrc = mSources.get(key);
+ boolean newSrc = false;
if (mySrc == null) {
mySrc = new SourceState(key);
mSources.put(key, mySrc);
+ newSrc = true;
+ }
+ if (VALIDATE_TIMES) {
+ Slog.w(TAG, "Adding tot duration " + mySrc.mDuration + "+"
+ + otherSrc.mDuration
+ + (newSrc ? " (new)" : " (old)") + " (total "
+ + origDuration + "+" + other.mTotalDuration + ") in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName);
+ if ((mySrc.mDuration + otherSrc.mDuration) > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source tot duration " + mySrc.mDuration + "+"
+ + otherSrc.mDuration
+ + (newSrc ? " (new)" : " (old)") + " exceeds total "
+ + origDuration + "+" + other.mTotalDuration + " in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ if (mySrc.mActiveDurations == null && otherSrc.mActiveDurations == null) {
+ Slog.w(TAG, "Adding act duration " + mySrc.mActiveDuration
+ + "+" + otherSrc.mActiveDuration
+ + (newSrc ? " (new)" : " (old)") + " (total "
+ + origDuration + "+" + other.mTotalDuration + ") in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName);
+ if ((mySrc.mActiveDuration + otherSrc.mActiveDuration) > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source act duration " + mySrc.mActiveDuration + "+"
+ + otherSrc.mActiveDuration
+ + (newSrc ? " (new)" : " (old)") + " exceeds total "
+ + origDuration + "+" + other.mTotalDuration + " in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ }
}
mySrc.mCount += otherSrc.mCount;
mySrc.mDuration += otherSrc.mDuration;
mySrc.mActiveCount += otherSrc.mActiveCount;
- if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+ if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) {
// Only need to do anything if the other one has some duration data.
- if (mySrc.mDurations != null) {
+ if (mySrc.mActiveDurations != null) {
// If the target already has multiple durations, just add in whatever
// we have in the other.
- if (otherSrc.mDurations != null) {
- mySrc.mDurations.addDurations(otherSrc.mDurations);
+ if (otherSrc.mActiveDurations != null) {
+ mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
} else {
- mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
otherSrc.mActiveDuration);
}
- } else if (otherSrc.mDurations != null) {
+ } else if (otherSrc.mActiveDurations != null) {
// The other one has multiple durations, but we don't. Expand to
// multiple durations and copy over.
mySrc.makeDurations();
- mySrc.mDurations.addDurations(otherSrc.mDurations);
+ mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
if (mySrc.mActiveDuration != 0) {
- mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+ mySrc.mActiveDuration);
mySrc.mActiveDuration = 0;
mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
}
@@ -334,13 +441,14 @@ public final class AssociationState {
// Both have a single inline duration... we can either add them together,
// or need to expand to multiple durations.
if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
- mySrc.mDuration += otherSrc.mDuration;
+ mySrc.mActiveDuration += otherSrc.mActiveDuration;
} else {
// The two have durations with different proc states, need to turn
// in to multiple durations.
mySrc.makeDurations();
- mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
- mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+ mySrc.mActiveDuration);
+ mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
otherSrc.mActiveDuration);
mySrc.mActiveDuration = 0;
mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
@@ -355,12 +463,13 @@ public final class AssociationState {
}
public boolean isInUse() {
- return mNumActive > 0;
+ return mTotalNesting > 0;
}
public void resetSafely(long now) {
if (!isInUse()) {
mSources.clear();
+ mTotalCount = mTotalActiveCount = 0;
} else {
// We have some active sources... clear out everything but those.
for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
@@ -376,15 +485,28 @@ public final class AssociationState {
src.mActiveCount = 0;
}
src.mActiveDuration = 0;
- src.mDurations = null;
+ src.mActiveDurations = null;
} else {
mSources.removeAt(isrc);
}
}
+ mTotalCount = 1;
+ mTotalStartUptime = now;
+ if (mTotalActiveNesting > 0) {
+ mTotalActiveCount = 1;
+ mTotalActiveStartUptime = now;
+ } else {
+ mTotalActiveCount = 0;
+ }
}
+ mTotalDuration = mTotalActiveDuration = 0;
}
public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
+ out.writeInt(mTotalCount);
+ out.writeLong(mTotalDuration);
+ out.writeInt(mTotalActiveCount);
+ out.writeLong(mTotalActiveDuration);
final int NSRC = mSources.size();
out.writeInt(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
@@ -396,9 +518,9 @@ public final class AssociationState {
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
- if (src.mDurations != null) {
+ if (src.mActiveDurations != null) {
out.writeInt(1);
- src.mDurations.writeToParcel(out);
+ src.mActiveDurations.writeToParcel(out);
} else {
out.writeInt(0);
out.writeInt(src.mActiveProcState);
@@ -412,6 +534,10 @@ public final class AssociationState {
* caused it to fail.
*/
public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
+ mTotalCount = in.readInt();
+ mTotalDuration = in.readLong();
+ mTotalActiveCount = in.readInt();
+ mTotalActiveDuration = in.readLong();
final int NSRC = in.readInt();
if (NSRC < 0 || NSRC > 100000) {
return "Association with bad src count: " + NSRC;
@@ -427,13 +553,29 @@ public final class AssociationState {
src.mActiveCount = in.readInt();
if (in.readInt() != 0) {
src.makeDurations();
- if (!src.mDurations.readFromParcel(in)) {
+ if (!src.mActiveDurations.readFromParcel(in)) {
return "Duration table corrupt: " + key + " <- " + src;
}
} else {
src.mActiveProcState = in.readInt();
src.mActiveDuration = in.readLong();
}
+ if (VALIDATE_TIMES) {
+ if (src.mDuration > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Reading tot duration " + src.mDuration
+ + " exceeds total " + mTotalDuration + " in source "
+ + src.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ if (src.mActiveDurations == null && src.mActiveDuration > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Reading act duration " + src.mActiveDuration
+ + " exceeds total " + mTotalDuration + " in source "
+ + src.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ }
mSources.put(key, src);
}
return null;
@@ -448,19 +590,30 @@ public final class AssociationState {
src.mStartUptime = nowUptime;
}
if (src.mActiveStartUptime > 0) {
- final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
- if (src.mDurations != null) {
- src.mDurations.addDuration(src.mActiveProcState, duration);
+ final long addedDuration = nowUptime - src.mActiveStartUptime;
+ src.mActiveStartUptime = nowUptime;
+ if (src.mActiveDurations != null) {
+ src.mActiveDurations.addDuration(src.mActiveProcState, addedDuration);
} else {
- src.mActiveDuration = duration;
+ src.mActiveDuration += addedDuration;
}
- src.mActiveStartUptime = nowUptime;
}
}
+ if (mTotalNesting > 0) {
+ mTotalDuration += nowUptime - mTotalStartUptime;
+ mTotalStartUptime = nowUptime;
+ }
+ if (mTotalActiveNesting > 0) {
+ mTotalActiveDuration += nowUptime - mTotalActiveStartUptime;
+ mTotalActiveStartUptime = nowUptime;
+ }
}
}
public boolean hasProcessOrPackage(String procName) {
+ if (mProcessName.equals(procName)) {
+ return true;
+ }
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
@@ -471,22 +624,110 @@ public final class AssociationState {
return false;
}
- public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
- long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
- if (dumpAll) {
- pw.print(prefix);
- pw.print("mNumActive=");
- pw.println(mNumActive);
+ static final Comparator<Pair<SourceKey, SourceDumpContainer>> ASSOCIATION_COMPARATOR =
+ (o1, o2) -> {
+ if (o1.second.mActiveTime != o2.second.mActiveTime) {
+ return o1.second.mActiveTime > o2.second.mActiveTime ? -1 : 1;
+ }
+ if (o1.second.mTotalTime != o2.second.mTotalTime) {
+ return o1.second.mTotalTime > o2.second.mTotalTime ? -1 : 1;
+ }
+ if (o1.first.mUid != o2.first.mUid) {
+ return o1.first.mUid < o2.first.mUid ? -1 : 1;
}
+ if (o1.first.mProcess != o2.first.mProcess) {
+ int diff = o1.first.mProcess.compareTo(o2.first.mProcess);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
+ };
+
+ public ArrayList<Pair<SourceKey, SourceDumpContainer>> createSortedAssociations(long now,
+ long totalTime) {
final int NSRC = mSources.size();
+ ArrayList<Pair<SourceKey, SourceDumpContainer>> sources = new ArrayList<>(NSRC);
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)
- && !reqPackage.equals(key.mPackage)) {
- continue;
+ final SourceDumpContainer cont = new SourceDumpContainer(src);
+ long duration = src.mDuration;
+ if (src.mNesting > 0) {
+ duration += now - src.mStartUptime;
}
- pw.print(prefixInner);
+ cont.mTotalTime = duration;
+ cont.mActiveTime = dumpTime(null, null, src, totalTime, now, false, false);
+ if (cont.mActiveTime < 0) {
+ cont.mActiveTime = -cont.mActiveTime;
+ }
+ sources.add(new Pair<>(mSources.keyAt(isrc), cont));
+ }
+ Collections.sort(sources, ASSOCIATION_COMPARATOR);
+ return sources;
+ }
+
+ public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+ ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime,
+ String reqPackage, boolean dumpDetails, boolean dumpAll) {
+ final String prefixInnerInner = prefixInner + " ";
+ long totalDuration = mTotalActiveDuration;
+ if (mTotalActiveNesting > 0) {
+ totalDuration += now - mTotalActiveStartUptime;
+ }
+ if (totalDuration > 0 || mTotalActiveCount != 0) {
+ pw.print(prefix);
+ pw.print("Active count ");
+ pw.print(mTotalActiveCount);
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(totalDuration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+ pw.println();
+ }
+ if (dumpAll && mTotalActiveNesting != 0) {
+ pw.print(prefix);
+ pw.print("mTotalActiveNesting=");
+ pw.print(mTotalActiveNesting);
+ pw.print(" mTotalActiveStartUptime=");
+ TimeUtils.formatDuration(mTotalActiveStartUptime, now, pw);
+ pw.println();
+ }
+ totalDuration = mTotalDuration;
+ if (mTotalNesting > 0) {
+ totalDuration += now - mTotalStartUptime;
+ }
+ if (totalDuration > 0 || mTotalCount != 0) {
+ pw.print(prefix);
+ pw.print("Total count ");
+ pw.print(mTotalCount);
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(totalDuration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+ pw.println();
+ }
+ if (dumpAll && mTotalNesting != 0) {
+ pw.print(prefix);
+ pw.print("mTotalNesting=");
+ pw.print(mTotalNesting);
+ pw.print(" mTotalStartUptime=");
+ TimeUtils.formatDuration(mTotalStartUptime, now, pw);
+ pw.println();
+ }
+ final int NSRC = sources.size();
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final SourceKey key = sources.get(isrc).first;
+ final SourceDumpContainer cont = sources.get(isrc).second;
+ final SourceState src = cont.mState;
+ pw.print(prefix);
pw.print("<- ");
pw.print(key.mProcess);
pw.print("/");
@@ -496,24 +737,69 @@ public final class AssociationState {
pw.print(key.mPackage);
pw.print(")");
}
+ // If we are skipping this one, we still print the first line just to give
+ // context for the others (so it is clear the total times for the overall
+ // association come from other sources whose times are not shown).
+ if (reqPackage != null && !reqPackage.equals(key.mProcess)
+ && !reqPackage.equals(key.mPackage)) {
+ pw.println();
+ continue;
+ }
pw.println(":");
+ if (src.mActiveCount != 0 || src.mActiveDurations != null || src.mActiveDuration != 0
+ || src.mActiveStartUptime != 0) {
+ pw.print(prefixInner);
+ pw.print(" Active count ");
+ pw.print(src.mActiveCount);
+ if (dumpDetails) {
+ if (dumpAll) {
+ if (src.mActiveDurations != null) {
+ pw.print(" (multi-state)");
+ } else if (src.mActiveProcState >= ProcessStats.STATE_PERSISTENT) {
+ pw.print(" (");
+ pw.print(DumpUtils.STATE_NAMES[src.mActiveProcState]);
+ pw.print(")");
+ } else {
+ pw.print(" (*UNKNOWN STATE*)");
+ }
+ }
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(cont.mActiveTime, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) cont.mActiveTime / (double) totalTime);
+ if (src.mActiveStartUptime != 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ if (src.mActiveDurations != null) {
+ dumpTime(pw, prefixInnerInner, src, totalTime, now, dumpDetails, dumpAll);
+ }
+ } else {
+ pw.print(": ");
+ dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+ }
+ }
pw.print(prefixInner);
pw.print(" Total count ");
pw.print(src.mCount);
- long duration = src.mDuration;
- if (src.mNesting > 0) {
- duration += now - src.mStartUptime;
- }
if (dumpAll) {
- pw.print(": Duration ");
- TimeUtils.formatDuration(duration, pw);
+ pw.print(": ");
+ TimeUtils.formatDuration(cont.mTotalTime, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+ DumpUtils.printPercent(pw, (double) cont.mTotalTime / (double) totalTime);
if (src.mNesting > 0) {
pw.print(" (running");
+ if (dumpAll) {
+ pw.print(" nest=");
+ pw.print(src.mNesting);
+ }
if (src.mProcState != ProcessStats.STATE_NOTHING) {
pw.print(" / ");
pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
@@ -523,23 +809,6 @@ public final class AssociationState {
pw.print(")");
}
pw.println();
- if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
- || src.mActiveStartUptime != 0) {
- pw.print(prefixInner);
- pw.print(" Active count ");
- pw.print(src.mActiveCount);
- if (dumpDetails) {
- if (dumpAll) {
- pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
- }
- pw.println(":");
- dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
- } else {
- pw.print(": ");
- dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
- pw.println();
- }
- }
if (dumpAll) {
if (src.mInTrackingList) {
pw.print(prefixInner);
@@ -565,7 +834,6 @@ public final class AssociationState {
duration = -duration;
}
if (dumpAll) {
- pw.print("Duration ");
TimeUtils.formatDuration(duration, pw);
pw.print(" / ");
} else {
@@ -584,10 +852,10 @@ public final class AssociationState {
boolean isRunning = false;
for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
long time;
- if (src.mDurations != null) {
- time = src.mDurations.getValueForId((byte)iprocstate);
+ if (src.mActiveDurations != null) {
+ time = src.mActiveDurations.getValueForId((byte) iprocstate);
} else {
- time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+ time = src.mActiveProcState == iprocstate ? src.mActiveDuration : 0;
}
final String running;
if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
@@ -600,11 +868,9 @@ public final class AssociationState {
if (time != 0) {
if (pw != null) {
pw.print(prefix);
- pw.print(" ");
pw.print(DumpUtils.STATE_LABELS[iprocstate]);
pw.print(": ");
if (dumpAll) {
- pw.print("Duration ");
TimeUtils.formatDuration(time, pw);
pw.print(" / ");
} else {
@@ -619,21 +885,6 @@ public final class AssociationState {
totalTime += time;
}
}
- if (totalTime != 0 && pw != null) {
- pw.print(prefix);
- pw.print(" ");
- pw.print(DumpUtils.STATE_LABEL_TOTAL);
- pw.print(": ");
- if (dumpAll) {
- pw.print("Duration ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.print(" / ");
- } else {
- pw.print("time ");
- }
- DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
- pw.println();
- }
return isRunning ? -totalTime : totalTime;
}
@@ -667,11 +918,11 @@ public final class AssociationState {
pw.print(",");
pw.print(src.mActiveCount);
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
- if (src.mDurations != null) {
- final int N = src.mDurations.getKeyCount();
+ if (src.mActiveDurations != null) {
+ final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
- final int dkey = src.mDurations.getKeyAt(i);
- duration = src.mDurations.getValue(dkey);
+ final int dkey = src.mActiveDurations.getKeyAt(i);
+ duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
@@ -718,11 +969,11 @@ public final class AssociationState {
src.mActiveCount);
}
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
- if (src.mDurations != null) {
- final int N = src.mDurations.getKeyCount();
+ if (src.mActiveDurations != null) {
+ final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
- final int dkey = src.mDurations.getKeyAt(i);
- duration = src.mDurations.getValue(dkey);
+ final int dkey = src.mActiveDurations.getKeyAt(i);
+ duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 8e88c510ec31..875cff8d0988 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -31,6 +31,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -48,6 +49,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -179,7 +181,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 = 36;
+ private static final int PARCEL_VERSION = 38;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -196,6 +198,9 @@ public final class ProcessStats implements Parcelable {
public int mMemFactor = STATE_NOTHING;
public long mStartTime;
+ // Number of individual stats that have been aggregated to create this one.
+ public int mNumAggregated = 1;
+
public long mTimePeriodStartClock;
public long mTimePeriodStartRealtime;
public long mTimePeriodEndRealtime;
@@ -348,6 +353,8 @@ public final class ProcessStats implements Parcelable {
mSysMemUsage.mergeStats(other.mSysMemUsage);
+ mNumAggregated += other.mNumAggregated;
+
if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
mTimePeriodStartClock = other.mTimePeriodStartClock;
mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
@@ -569,6 +576,7 @@ public final class ProcessStats implements Parcelable {
}
private void resetCommon() {
+ mNumAggregated = 1;
mTimePeriodStartClock = System.currentTimeMillis();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
@@ -845,6 +853,7 @@ public final class ProcessStats implements Parcelable {
}
}
+ out.writeInt(mNumAggregated);
out.writeLong(mTimePeriodStartClock);
out.writeLong(mTimePeriodStartRealtime);
out.writeLong(mTimePeriodEndRealtime);
@@ -1031,6 +1040,7 @@ public final class ProcessStats implements Parcelable {
mIndexToCommonString = new ArrayList<String>();
+ mNumAggregated = in.readInt();
mTimePeriodStartClock = in.readLong();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = in.readLong();
@@ -1457,15 +1467,79 @@ public final class ProcessStats implements Parcelable {
}
}
+ final class AssociationDumpContainer {
+ final AssociationState mState;
+ ArrayList<Pair<AssociationState.SourceKey, AssociationState.SourceDumpContainer>> mSources;
+ long mTotalTime;
+ long mActiveTime;
+
+ AssociationDumpContainer(AssociationState state) {
+ mState = state;
+ }
+ }
+
+ static final Comparator<AssociationDumpContainer> ASSOCIATION_COMPARATOR = (o1, o2) -> {
+ int diff = o1.mState.getProcessName().compareTo(o2.mState.getProcessName());
+ if (diff != 0) {
+ return diff;
+ }
+ if (o1.mActiveTime != o2.mActiveTime) {
+ return o1.mActiveTime > o2.mActiveTime ? -1 : 1;
+ }
+ if (o1.mTotalTime != o2.mTotalTime) {
+ return o1.mTotalTime > o2.mTotalTime ? -1 : 1;
+ }
+ diff = o1.mState.getName().compareTo(o2.mState.getName());
+ if (diff != 0) {
+ return diff;
+ }
+ return 0;
+ };
+
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
- boolean sepNeeded = false;
+ pw.print(" Start time: ");
+ pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+ pw.println();
+ pw.print(" Total uptime: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
+ - mTimePeriodStartUptime, pw);
+ pw.println();
+ pw.print(" Total elapsed time: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+ - mTimePeriodStartRealtime, pw);
+ boolean partial = true;
+ if ((mFlags & FLAG_SHUTDOWN) != 0) {
+ pw.print(" (shutdown)");
+ partial = false;
+ }
+ if ((mFlags & FLAG_SYSPROPS) != 0) {
+ pw.print(" (sysprops)");
+ partial = false;
+ }
+ if ((mFlags & FLAG_COMPLETE) != 0) {
+ pw.print(" (complete)");
+ partial = false;
+ }
+ if (partial) {
+ pw.print(" (partial)");
+ }
+ if (mHasSwappedOutPss) {
+ pw.print(" (swapped-out-pss)");
+ }
+ pw.print(' ');
+ pw.print(mRuntime);
+ pw.println();
+ pw.print(" Aggregated over: ");
+ pw.println(mNumAggregated);
if (mSysMemUsage.getKeyCount() > 0) {
+ pw.println();
pw.println("System memory usage:");
mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
- sepNeeded = true;
}
boolean printedHeader = false;
if ((section & REPORT_PKG_STATS) != 0) {
@@ -1485,8 +1559,8 @@ public final class ProcessStats implements Parcelable {
final int NASCS = pkgState.mAssociations.size();
final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
boolean onlyAssociations = false;
+ boolean procMatch = false;
if (!pkgMatch) {
- boolean procMatch = false;
for (int iproc = 0; iproc < NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
if (reqPackage.equals(proc.getName())) {
@@ -1511,10 +1585,9 @@ public final class ProcessStats implements Parcelable {
}
if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
if (!printedHeader) {
- if (sepNeeded) pw.println();
+ pw.println();
pw.println("Per-Package Stats:");
printedHeader = true;
- sepNeeded = true;
}
pw.print(" * ");
pw.print(pkgName);
@@ -1597,6 +1670,8 @@ public final class ProcessStats implements Parcelable {
}
}
if ((section & REPORT_PKG_ASC_STATS) != 0) {
+ ArrayList<AssociationDumpContainer> associations =
+ new ArrayList<>(NASCS);
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
@@ -1604,6 +1679,18 @@ public final class ProcessStats implements Parcelable {
continue;
}
}
+ final AssociationDumpContainer cont =
+ new AssociationDumpContainer(asc);
+ cont.mSources = asc.createSortedAssociations(now, totalTime);
+ cont.mTotalTime = asc.getTotalDuration(now);
+ cont.mActiveTime = asc.getActiveDuration(now);
+ associations.add(cont);
+ }
+ Collections.sort(associations, ASSOCIATION_COMPARATOR);
+ final int NCONT = associations.size();
+ for (int iasc = 0; iasc < NCONT; iasc++) {
+ final AssociationDumpContainer cont = associations.get(iasc);
+ final AssociationState asc = cont.mState;
if (activeOnly && !asc.isInUse()) {
pw.print(" (Not active association: ");
pw.print(pkgState.mAssociations.keyAt(iasc));
@@ -1615,13 +1702,15 @@ public final class ProcessStats implements Parcelable {
} else {
pw.print(" * Asc ");
}
- pw.print(pkgState.mAssociations.keyAt(iasc));
+ pw.print(cont.mState.getName());
pw.println(":");
pw.print(" Process: ");
pw.println(asc.getProcessName());
asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, onlyAssociations ? reqPackage : null,
- dumpDetails, dumpAll);
+ cont.mSources, now, totalTime,
+ onlyAssociations && !pkgMatch && !procMatch
+ && !asc.getProcessName().equals(reqPackage)
+ ? reqPackage : null, dumpDetails, dumpAll);
}
}
}
@@ -1651,10 +1740,7 @@ public final class ProcessStats implements Parcelable {
continue;
}
numShownProcs++;
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
+ pw.println();
if (!printedHeader) {
pw.println("Multi-Package Common Processes:");
printedHeader = true;
@@ -1684,11 +1770,7 @@ public final class ProcessStats implements Parcelable {
}
if (dumpAll) {
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
-
+ pw.println();
if (mTrackingAssociations.size() > 0) {
pw.println();
pw.println("Tracking associations:");
@@ -1734,9 +1816,7 @@ public final class ProcessStats implements Parcelable {
}
}
- if (sepNeeded) {
- pw.println();
- }
+ pw.println();
if (dumpSummary) {
pw.println("Process summary:");
dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1861,41 +1941,6 @@ public final class ProcessStats implements Parcelable {
pw.print("x over ");
TimeUtils.formatDuration(mExternalSlowPssTime, pw);
pw.println();
- pw.println();
- pw.print(" Start time: ");
- pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
- pw.println();
- pw.print(" Total uptime: ");
- TimeUtils.formatDuration(
- (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
- - mTimePeriodStartUptime, pw);
- pw.println();
- pw.print(" Total elapsed time: ");
- TimeUtils.formatDuration(
- (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
- - mTimePeriodStartRealtime, pw);
- boolean partial = true;
- if ((mFlags&FLAG_SHUTDOWN) != 0) {
- pw.print(" (shutdown)");
- partial = false;
- }
- if ((mFlags&FLAG_SYSPROPS) != 0) {
- pw.print(" (sysprops)");
- partial = false;
- }
- if ((mFlags&FLAG_COMPLETE) != 0) {
- pw.print(" (complete)");
- partial = false;
- }
- if (partial) {
- pw.print(" (partial)");
- }
- if (mHasSwappedOutPss) {
- pw.print(" (swapped-out-pss)");
- }
- pw.print(' ');
- pw.print(mRuntime);
- pw.println();
}
void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, String prcLabel,
diff --git a/services/core/java/com/android/server/wm/WindowHashMap.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl
index 49bba4145996..434c1b819582 100644
--- a/services/core/java/com/android/server/wm/WindowHashMap.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,18 +11,9 @@
* 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
+ * limitations under the License.
*/
-package com.android.server.wm;
+package com.android.internal.compat;
-import android.os.IBinder;
-
-import java.util.HashMap;
-
-/**
- * Subclass of HashMap such that we can instruct the compiler to boost our thread priority when
- * locking this class. See makefile.
- */
-class WindowHashMap extends HashMap<IBinder, WindowState> {
-}
+parcelable CompatibilityChangeConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
new file mode 100644
index 000000000000..fd2ada08edc1
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+
+import android.compat.Compatibility.ChangeConfig;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Parcelable containing compat config overrides for a given application.
+ * @hide
+ */
+public final class CompatibilityChangeConfig implements Parcelable {
+ private final ChangeConfig mChangeConfig;
+
+ public CompatibilityChangeConfig(ChangeConfig changeConfig) {
+ mChangeConfig = changeConfig;
+ }
+
+ /**
+ * Changes forced to be enabled.
+ */
+ public Set<Long> enabledChanges() {
+ return mChangeConfig.forceEnabledSet();
+ }
+
+ /**
+ * Changes forced to be disabled.
+ */
+ public Set<Long> disabledChanges() {
+ return mChangeConfig.forceDisabledSet();
+ }
+
+ private CompatibilityChangeConfig(Parcel in) {
+ long[] enabledArray = in.createLongArray();
+ long[] disabledArray = in.createLongArray();
+ Set<Long> enabled = toLongSet(enabledArray);
+ Set<Long> disabled = toLongSet(disabledArray);
+ mChangeConfig = new ChangeConfig(enabled, disabled);
+ }
+
+ private static Set<Long> toLongSet(long[] values) {
+ Set<Long> ret = new HashSet<>();
+ for (long value: values) {
+ ret.add(value);
+ }
+ return ret;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ long[] enabled = mChangeConfig.forceEnabledChangesArray();
+ long[] disabled = mChangeConfig.forceDisabledChangesArray();
+
+ dest.writeLongArray(enabled);
+ dest.writeLongArray(disabled);
+ }
+
+ public static final Parcelable.Creator<CompatibilityChangeConfig> CREATOR =
+ new Parcelable.Creator<CompatibilityChangeConfig>() {
+
+ @Override
+ public CompatibilityChangeConfig createFromParcel(Parcel in) {
+ return new CompatibilityChangeConfig(in);
+ }
+
+ @Override
+ public CompatibilityChangeConfig[] newArray(int size) {
+ return new CompatibilityChangeConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 4d8378a34599..4099cfa51b33 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -18,6 +18,8 @@ package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
+parcelable CompatibilityChangeConfig;
+
/**
* Platform private API for talking with the PlatformCompat service.
*
@@ -125,4 +127,21 @@ interface IPlatformCompat
* @return {@code true} if the change is enabled for the current app.
*/
boolean isChangeEnabledByUid(long changeId, int uid);
-} \ No newline at end of file
+
+ /**
+ * Add overrides to compatibility changes.
+ *
+ * @param overrides Parcelable containing the compat change overrides to be applied.
+ * @param packageName The package name of the app whose changes will be overridden.
+ *
+ */
+ void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
+
+ /**
+ * Revert overrides to compatibility changes.
+ *
+ * @param packageName The package name of the app whose overrides will be cleared.
+ *
+ */
+ void clearOverrides(in String packageName);
+}
diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS
new file mode 100644
index 000000000000..2b7cdb0cbce9
--- /dev/null
+++ b/core/java/com/android/internal/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0feab7f61918..ce405feb5985 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -117,6 +117,7 @@ cc_library_shared {
"android_view_RenderNodeAnimator.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
+ "android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
"android_view_TextureView.cpp",
"android_view_VelocityTracker.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d476d2d2c35c..3497f920e6a4 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -115,6 +115,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_content_res_ApkAssets(JNIEnv* env);
+extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
@@ -1458,10 +1459,9 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_opengl_jni_GLES31),
REG_JNI(register_android_opengl_jni_GLES31Ext),
REG_JNI(register_android_opengl_jni_GLES32),
-
REG_JNI(register_android_graphics_classes),
+ REG_JNI(register_android_graphics_BLASTBufferQueue),
REG_JNI(register_android_graphics_GraphicBuffer),
-
REG_JNI(register_android_database_CursorWindow),
REG_JNI(register_android_database_SQLiteConnection),
REG_JNI(register_android_database_SQLiteGlobal),
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index bd4862dfb08d..637025329e37 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_RESOURCES
+#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "android-base/unique_fd.h"
@@ -32,7 +33,7 @@ using ::android::base::unique_fd;
namespace android {
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
- jboolean force_shared_lib, jboolean overlay) {
+ jboolean force_shared_lib, jboolean overlay, jboolean for_loader) {
ScopedUtfChars path(env, java_path);
if (path.c_str() == nullptr) {
return 0;
@@ -46,7 +47,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
} else if (force_shared_lib) {
apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
} else {
- apk_assets = ApkAssets::Load(path.c_str(), system);
+ apk_assets = ApkAssets::Load(path.c_str(), system, for_loader);
}
if (apk_assets == nullptr) {
@@ -58,7 +59,8 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
}
static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
- jstring friendly_name, jboolean system, jboolean force_shared_lib) {
+ jstring friendly_name, jboolean system, jboolean force_shared_lib,
+ jboolean for_loader) {
ScopedUtfChars friendly_name_utf8(env, friendly_name);
if (friendly_name_utf8.c_str() == nullptr) {
return 0;
@@ -80,7 +82,9 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri
std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
friendly_name_utf8.c_str(),
- system, force_shared_lib);
+ system, force_shared_lib,
+ for_loader);
+
if (apk_assets == nullptr) {
std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
friendly_name_utf8.c_str(), dup_fd.get());
@@ -90,6 +94,60 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri
return reinterpret_cast<jlong>(apk_assets.release());
}
+static jlong NativeLoadArsc(JNIEnv* env, jclass /*clazz*/, jstring java_path,
+ jboolean for_loader) {
+ ScopedUtfChars path(env, java_path);
+ if (path.c_str() == nullptr) {
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsArsc(%s)", path.c_str()).c_str());
+
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadArsc(path.c_str(), for_loader);
+
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadArscFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
+ jstring friendly_name, jboolean for_loader) {
+ ScopedUtfChars friendly_name_utf8(env, friendly_name);
+ if (friendly_name_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsArscFd(%d)", fd).c_str());
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<const ApkAssets> apk_assets =
+ ApkAssets::LoadArsc(std::move(dup_fd), friendly_name_utf8.c_str(), for_loader);
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path from fd %d", fd);
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jboolean for_loader) {
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadEmpty(for_loader);
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
delete reinterpret_cast<ApkAssets*>(ptr);
}
@@ -138,9 +196,13 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil
// JNI registration.
static const JNINativeMethod gApkAssetsMethods[] = {
- {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad},
- {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J",
+ {"nativeLoad", "(Ljava/lang/String;ZZZZ)J", (void*)NativeLoad},
+ {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZZ)J",
(void*)NativeLoadFromFd},
+ {"nativeLoadArsc", "(Ljava/lang/String;Z)J", (void*)NativeLoadArsc},
+ {"nativeLoadArscFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)J",
+ (void*)NativeLoadArscFromFd},
+ {"nativeLoadEmpty", "(Z)J", (void*)NativeLoadEmpty},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
new file mode 100644
index 000000000000..185e58160adf
--- /dev/null
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BLASTBufferQueue"
+
+#include <nativehelper/JNIHelp.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <gui/BLASTBufferQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jlong surfaceControl, jlong width, jlong height) {
+ sp<BLASTBufferQueue> queue = new BLASTBufferQueue(
+ reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+ queue->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(queue.get());
+}
+
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->decStrong((void*)nativeCreate);
+}
+
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return android_view_Surface_createFromIGraphicBufferProducer(env, queue->getIGraphicBufferProducer());
+}
+
+static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+ queue->setNextTransaction(transaction);
+}
+
+static void nativeUpdate(JNIEnv*env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+}
+
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeCreate", "(JJJ)J",
+ (void*)nativeCreate },
+ { "nativeGetSurface", "(J)Landroid/view/Surface;",
+ (void*)nativeGetSurface },
+ { "nativeDestroy", "(J)V",
+ (void*)nativeDestroy },
+ { "nativeSetNextTransaction", "(JJ)V",
+ (void*)nativeSetNextTransaction },
+ { "nativeUpdate", "(JJJJ)V",
+ (void*)nativeUpdate }
+};
+
+int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
+ gMethods, NELEM(gMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 9445319e47ec..d62d2d967d85 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -139,8 +139,8 @@ static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
"nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
};
-jfieldID otherStats_field;
-jfieldID hasSwappedOutPss_field;
+static jfieldID otherStats_field;
+static jfieldID hasSwappedOutPss_field;
struct stats_t {
int pss;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index daf33f61105c..c7b36d0f8fc9 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -108,7 +108,7 @@ static struct arraymap_offsets_t {
jmethodID put;
} gArrayMapOffsets;
-jclass g_stringClass = nullptr;
+static jclass g_stringClass = nullptr;
// ----------------------------------------------------------------------------
@@ -763,6 +763,41 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint
return reinterpret_cast<jlong>(xml_tree.release());
}
+static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int jcookie,
+ jobject file_descriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAssetFd(%d)", fd).c_str());
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ base::unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<Asset>
+ asset(Asset::createFromFd(dup_fd.release(), nullptr, Asset::AccessMode::ACCESS_BUFFER));
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+
+ // May be nullptr.
+ const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
+
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
jshort density, jobject typed_value,
jboolean resolve_references) {
@@ -1564,6 +1599,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenNonAssetFd},
{"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+ {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
// AssetManager resource methods.
{"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a8d307..485709523e66 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/listPreferredItemHeightSmall"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
android:background="?attr/activatedBackgroundIndicator">
<!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
android:layout_width="@dimen/resolver_icon_size"
android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|center_vertical"
- android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:layout_marginEnd="@dimen/resolver_icon_margin"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="start|center_vertical"
android:orientation="vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="@dimen/resolver_edge_margin"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
- android:textColor="?attr/textColorPrimary"
+ android:layout_gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:textSize="16sp"
android:minLines="1"
android:maxLines="1"
android:ellipsize="marquee" />
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:layout_gravity="start|center_vertical"
+ android:textSize="14sp"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd72870d..0a35edc42329 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:text="@string/use_a_different_app"
- android:minHeight="56dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:elevation="8dp"
- />
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd420746e8a..6e45e7a4c509 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:elevation="8dp"
- android:background="?attr/colorBackgroundFloating">
+ android:elevation="@dimen/resolver_elevation"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:background="@drawable/bottomsheet_background">
<TextView
android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
<TextView
android:id="@+id/title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
android:layout_below="@id/profile_button"
android:layout_alignParentStart="true"
- android:paddingBottom="8dp" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ android:gravity="start|center_vertical" />
</RelativeLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
+ android:scrollbarStyle="outsideOverlay"
android:scrollIndicators="top|bottom"
- android:divider="@null" />
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
+
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:layout_alwaysShow="true"
android:text="@string/noApplications"
android:padding="32dp"
@@ -102,18 +117,19 @@
android:background="?attr/colorBackgroundFloating"
android:paddingTop="@dimen/resolver_button_bar_spacing"
android:paddingBottom="@dimen/resolver_button_bar_spacing"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
+ android:textAllCaps="false"
android:enabled="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb9374e..dbba0b7bcc25 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:orientation="vertical"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp">
+ android:background="@drawable/bottomsheet_background"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal">
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin">
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/resolver_icon_size"
+ android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
android:src="@drawable/resolver_icon_placeholder"
android:scaleType="fitCenter" />
@@ -52,9 +52,11 @@
android:id="@+id/title"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_height="?attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
android:paddingEnd="16dp" />
@@ -107,21 +109,22 @@
android:orientation="horizontal"
android:layoutDirection="locale"
android:measureWithLargestChild="true"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingTop="@dimen/resolver_button_bar_spacing"
+ android:paddingBottom="@dimen/resolver_button_bar_spacing"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -130,29 +133,40 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick" />
</LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?attr/dividerVertical" />
</LinearLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
- android:divider="@null" />
-
+ android:scrollbarStyle="outsideOverlay"
+ android:scrollIndicators="top|bottom"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 381ed7f80602..a301702e5f9b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2840,6 +2840,9 @@
<!-- The name of the overlayable whose resources will be overlaid. -->
<attr name="targetName" />
+
+ <!-- The xml file that defines the target id to overlay value mappings. -->
+ <attr name="resourcesMap" format="reference" />
</declare-styleable>
<!-- Declaration of an {@link android.content.Intent} object in XML. May
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b62948..a01bbe38f296 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -750,7 +750,7 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
- <!-- chooser (sharesheet) spacing -->
+ <!-- chooser/resolver (sharesheet) spacing -->
<dimen name="chooser_corner_radius">8dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +759,15 @@
<dimen name="chooser_preview_image_font_size">20sp</dimen>
<dimen name="chooser_preview_image_border">1dp</dimen>
<dimen name="chooser_preview_width">-1px</dimen>
- <dimen name="resolver_icon_size">42dp</dimen>
- <dimen name="resolver_button_bar_spacing">8dp</dimen>
- <dimen name="resolver_badge_size">18dp</dimen>
<dimen name="chooser_target_width">90dp</dimen>
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+ <dimen name="resolver_icon_size">32dp</dimen>
+ <dimen name="resolver_button_bar_spacing">8dp</dimen>
+ <dimen name="resolver_badge_size">18dp</dimen>
+ <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_small_margin">18dp</dimen>
+ <dimen name="resolver_edge_margin">24dp</dimen>
+ <dimen name="resolver_elevation">1dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 96c0cf3fd86a..9724c41c1b16 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3000,6 +3000,8 @@
<public-group type="attr" first-id="0x01010607">
<public name="importantForContentCapture" />
<public name="forceQueryable" />
+ <!-- @hide @SystemApi -->
+ <public name="resourcesMap" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 363bc9ddd75c..c5a0dfca4bd1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3819,6 +3819,10 @@
<java-symbol type="dimen" name="resolver_icon_size"/>
<java-symbol type="dimen" name="resolver_badge_size"/>
<java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+ <java-symbol type="dimen" name="resolver_icon_margin"/>
+ <java-symbol type="dimen" name="resolver_small_margin"/>
+ <java-symbol type="dimen" name="resolver_edge_margin"/>
+ <java-symbol type="dimen" name="resolver_elevation"/>
<!-- For DropBox -->
<java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
diff --git a/core/tests/ResourceLoaderTests/Android.bp b/core/tests/ResourceLoaderTests/Android.bp
new file mode 100644
index 000000000000..53db8322f7b8
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/Android.bp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "FrameworksResourceLoaderTests",
+ srcs: [
+ "src/**/*.kt"
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ resource_zips: [ ":FrameworksResourceLoaderTestsAssets" ],
+ test_suites: ["device-tests"],
+ sdk_version: "test_current",
+ aaptflags: [
+ "--no-compress",
+ ],
+ data: [
+ ":FrameworksResourceLoaderTestsOverlay",
+ ":FrameworksResourceLoaderTestsSplitOne",
+ ":FrameworksResourceLoaderTestsSplitTwo",
+ ],
+ java_resources: [ "NonAsset.txt" ]
+}
+
+filegroup {
+ name: "FrameworksResourceLoaderTestsResources",
+ srcs: ["resources"],
+}
+
+genrule {
+ name: "FrameworksResourceLoaderTestsAssets",
+ srcs: [
+ ":framework-res",
+ ":FrameworksResourceLoaderTestsResources",
+ ],
+ tools: [ ":aapt2", ":soong_zip" ],
+ tool_files: [ "resources/compileAndLink.sh" ],
+ cmd: "$(location resources/compileAndLink.sh) $(location :aapt2) $(location :soong_zip) $(genDir) $(in) $(in)",
+ out: [ "out.zip" ]
+}
diff --git a/core/tests/ResourceLoaderTests/AndroidManifest.xml b/core/tests/ResourceLoaderTests/AndroidManifest.xml
new file mode 100644
index 000000000000..00b4ccbd8030
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Split loading is tested separately, so this must be marked isolated -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ android:isolatedSplits="true"
+ >
+
+ <uses-sdk android:minSdkVersion="29"/>
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+
+ <activity
+ android:name=".TestActivity"
+ android:configChanges="orientation"
+ />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="ResourceLoaderTests"
+ android:targetPackage="android.content.res.loader.test"
+ />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/AndroidTest.xml b/core/tests/ResourceLoaderTests/AndroidTest.xml
new file mode 100644
index 000000000000..702151d01110
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/AndroidTest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for ResourceLoaderTests">
+ <option name="test-tag" value="ResourceLoaderTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <!-- The following value cannot be multi-line as whitespace is parsed by the installer -->
+ <option name="split-apk-file-names"
+ value="FrameworksResourceLoaderTests.apk,FrameworksResourceLoaderTestsSplitOne.apk,FrameworksResourceLoaderTestsSplitTwo.apk" />
+ <option name="test-file-name" value="FrameworksResourceLoaderTestsOverlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="cmd overlay disable android.content.res.loader.test.overlay" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.content.res.loader.test" />
+ </test>
+</configuration>
diff --git a/core/tests/ResourceLoaderTests/NonAsset.txt b/core/tests/ResourceLoaderTests/NonAsset.txt
new file mode 100644
index 000000000000..5c0b2cc98d64
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/NonAsset.txt
@@ -0,0 +1 @@
+Outside assets directory
diff --git a/core/tests/ResourceLoaderTests/SplitOne/Android.bp b/core/tests/ResourceLoaderTests/SplitOne/Android.bp
new file mode 100644
index 000000000000..897897fbf254
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/Android.bp
@@ -0,0 +1,19 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTestsSplitOne"
+}
diff --git a/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml b/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml
new file mode 100644
index 000000000000..b14bd8600f31
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ split="split_one"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application android:hasCode="false" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml b/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml
new file mode 100644
index 000000000000..3c215ebc287c
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <public type="string" name="split_overlaid" id="0x7f040001" />
+ <string name="split_overlaid">Split ONE Overlaid</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/assets/Asset.txt b/core/tests/ResourceLoaderTests/assets/Asset.txt
new file mode 100644
index 000000000000..03f9a0fd146a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/assets/Asset.txt
@@ -0,0 +1 @@
+In assets directory
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar
new file mode 100644
index 000000000000..a12e33a34aee
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar b/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar
new file mode 100644
index 000000000000..182cbabadfe6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar
new file mode 100644
index 000000000000..e6b5f15b8a57
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar
new file mode 100644
index 000000000000..e9c743c60289
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar
new file mode 100644
index 000000000000..cd0536042662
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar
new file mode 100644
index 000000000000..dc8aa90385fd
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar
new file mode 100644
index 000000000000..8a672bac4685
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar
new file mode 100644
index 000000000000..56f3d1e385e4
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar
new file mode 100644
index 000000000000..663d3128dd54
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-test.jar b/core/tests/ResourceLoaderTests/lib/kotlin-test.jar
new file mode 100644
index 000000000000..5f6e4b8cc988
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-test.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/overlay/Android.bp b/core/tests/ResourceLoaderTests/overlay/Android.bp
new file mode 100644
index 000000000000..63e7e61d797a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+android_test {
+ name: "FrameworksResourceLoaderTestsOverlay",
+ sdk_version: "current",
+
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml b/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..942f7da9aa27
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test.overlay"
+ >
+
+ <application android:hasCode="false" />
+
+ <overlay android:targetPackage="android.content.res.loader.test" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml b/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml
new file mode 100644
index 000000000000..348bb353611a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+
+ <string name="loader_path_change_test">Overlaid</string>
+
+</resources>
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png
new file mode 100644
index 000000000000..efd71ee039e2
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml
new file mode 100644
index 000000000000..d1211c50a203
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#B2D2F2"
+ />
diff --git a/core/tests/ResourceLoaderTests/res/layout/layout.xml b/core/tests/ResourceLoaderTests/res/layout/layout.xml
new file mode 100644
index 000000000000..d59059b453d6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/layout/layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/res/values/strings.xml b/core/tests/ResourceLoaderTests/res/values/strings.xml
new file mode 100644
index 000000000000..28b8f73d45a6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+
+ <string name="loader_path_change_test">Not overlaid</string>
+ <string name="split_overlaid">Not overlaid</string>
+
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
new file mode 100644
index 000000000000..5dd8a966e2b7
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application/>
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
new file mode 100644
index 000000000000..5a92ae9e662b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Mocks the framework package name so that AAPT2 assigns the correct package -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application/>
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/resources/compileAndLink.sh b/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
new file mode 100755
index 000000000000..885f681f4261
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+aapt2=$1
+soong_zip=$2
+genDir=$3
+FRAMEWORK_RES_APK=$4
+inDir=$5
+
+# (String name, boolean retainFiles = false, String... files)
+function compileAndLink {
+ moduleName=$1
+ mkdir "$genDir"/out/"$moduleName"
+
+ args=""
+ for arg in "${@:4}"; do
+ if [[ $arg == res* ]]; then
+ args="$args $inDir/$arg"
+ else
+ args="$args $arg"
+ fi
+ done
+
+ $aapt2 compile -o "$genDir"/out/"$moduleName" $args
+
+ $aapt2 link \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest "$inDir"/"$3" \
+ -o "$genDir"/out/"$moduleName"/apk.apk \
+ "$genDir"/out/"$moduleName"/*.flat \
+ --no-compress
+
+ unzip -qq "$genDir"/out/"$moduleName"/apk.apk -d "$genDir"/out/"$moduleName"/unzip
+
+ if [[ "$2" == "APK_WITHOUT_FILE" || "$2" == "BOTH_WITHOUT_FILE" ]]; then
+ zip -q -d "$genDir"/out/"$moduleName"/apk.apk "res/*"
+ cp "$genDir"/out/"$moduleName"/apk.apk "$genDir"/output/raw/"$moduleName"Apk.apk
+ elif [[ "$2" == "APK" || "$2" == "BOTH" ]]; then
+ cp "$genDir"/out/"$moduleName"/apk.apk "$genDir"/output/raw/"$moduleName"Apk.apk
+ fi
+
+ if [[ "$2" == "ARSC" || "$2" == "BOTH" || "$2" == "BOTH_WITHOUT_FILE" ]]; then
+ zip -d "$genDir"/out/"$moduleName"/apk.apk "res/*"
+ cp "$genDir"/out/"$moduleName"/unzip/resources.arsc "$genDir"/output/raw/"$moduleName"Arsc.arsc
+ fi
+}
+
+rm -r "$genDir"/out
+rm -r "$genDir"/output
+rm -r "$genDir"/temp
+
+mkdir "$genDir"/out
+mkdir -p "$genDir"/output/raw
+mkdir -p "$genDir"/temp/res/drawable-nodpi
+mkdir -p "$genDir"/temp/res/layout
+
+compileAndLink stringOne BOTH AndroidManifestFramework.xml res/values/string_one.xml
+compileAndLink stringTwo BOTH AndroidManifestFramework.xml res/values/string_two.xml
+
+compileAndLink dimenOne BOTH AndroidManifestFramework.xml res/values/dimen_one.xml
+compileAndLink dimenTwo BOTH AndroidManifestFramework.xml res/values/dimen_two.xml
+
+compileAndLink drawableMdpiWithoutFile BOTH_WITHOUT_FILE AndroidManifestFramework.xml res/values/drawable_one.xml res/drawable-mdpi/ic_delete.png
+compileAndLink drawableMdpiWithFile APK AndroidManifestFramework.xml res/values/drawable_one.xml res/drawable-mdpi/ic_delete.png
+
+compileAndLink layoutWithoutFile BOTH_WITHOUT_FILE AndroidManifestFramework.xml res/values/activity_list_item_id.xml res/layout/activity_list_item.xml
+compileAndLink layoutWithFile APK AndroidManifestFramework.xml res/values/activity_list_item_id.xml res/layout/activity_list_item.xml
+
+cp -f "$inDir"/res/layout/layout_one.xml "$genDir"/temp/res/layout/layout.xml
+compileAndLink layoutOne ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
+cp -f "$genDir"/out/layoutOne/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutOne.xml
+
+cp -f "$inDir"/res/layout/layout_two.xml "$genDir"/temp/res/layout/layout.xml
+compileAndLink layoutTwo ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
+cp -f "$genDir"/out/layoutTwo/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutTwo.xml
+
+drawableNoDpi="/res/drawable-nodpi"
+inDirDrawableNoDpi="$inDir$drawableNoDpi"
+
+cp -f "$inDirDrawableNoDpi"/nonAssetDrawableOne.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
+compileAndLink nonAssetDrawableOne ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
+cp -f "$genDir"/out/nonAssetDrawableOne/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableOne.xml
+
+cp -f "$inDirDrawableNoDpi"/nonAssetDrawableTwo.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
+compileAndLink nonAssetDrawableTwo ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
+cp -f "$genDir"/out/nonAssetDrawableTwo/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableTwo.xml
+
+cp -f "$inDirDrawableNoDpi"/nonAssetBitmapGreen.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
+compileAndLink nonAssetBitmapGreen BOTH AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
+cp -f "$genDir"/out/nonAssetBitmapGreen/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapGreen.png
+
+cp -f "$inDirDrawableNoDpi"/nonAssetBitmapBlue.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
+compileAndLink nonAssetBitmapBlue ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
+cp -f "$genDir"/out/nonAssetBitmapBlue/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapBlue.png
+
+$soong_zip -o "$genDir"/out.zip -C "$genDir"/output/ -D "$genDir"/output/
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png b/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png
new file mode 100644
index 000000000000..f3e53d7596c1
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png
new file mode 100644
index 000000000000..5231d175569e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png
new file mode 100644
index 000000000000..671d6d00be31
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml
new file mode 100644
index 000000000000..f1a93d2d2f21
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#A3C3E3"
+ />
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml
new file mode 100644
index 000000000000..7c455a57fb0b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#3A3C3E"
+ />
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml b/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml
new file mode 100644
index 000000000000..d59059b453d6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml b/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml
new file mode 100644
index 000000000000..ede3838be8de
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml b/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml
new file mode 100644
index 000000000000..d8bff90d56d8
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml
new file mode 100644
index 000000000000..a552431e23be
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="layout" name="activity_list_item" id="0x01090000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml
new file mode 100644
index 000000000000..69ecf2316284
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="dimen" name="app_icon_size" id="0x01050000" />
+ <dimen name="app_icon_size">564716dp</dimen>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml b/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
new file mode 100644
index 000000000000..4d55deffbd2a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="dimen" name="app_icon_size" id="0x01050000" />
+ <dimen name="app_icon_size">565717dp</dimen>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
new file mode 100644
index 000000000000..b5b4dfd22231
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="drawable" name="ic_delete" id="0x0108001d" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml
new file mode 100644
index 000000000000..4962a07bc8c7
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <public type="layout" name="layout" id="0x7f020000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml
new file mode 100644
index 000000000000..38b152beb76f
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <public type="drawable" name="non_asset_bitmap" id="0x7f010000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml
new file mode 100644
index 000000000000..bdd6f58e5824
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <public type="drawable" name="non_asset_drawable" id="0x7f010001" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml
new file mode 100644
index 000000000000..4fc52723946e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="string" name="cancel" id="0x01040000" />
+ <string name="cancel">SomeRidiculouslyUnlikelyStringOne</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml b/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml
new file mode 100644
index 000000000000..3604d7b21cf5
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <public type="string" name="cancel" id="0x01040000" />
+ <string name="cancel">SomeRidiculouslyUnlikelyStringTwo</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/Android.bp b/core/tests/ResourceLoaderTests/splits/Android.bp
new file mode 100644
index 000000000000..4582808934df
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/Android.bp
@@ -0,0 +1,19 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTestsSplitTwo"
+}
diff --git a/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml b/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml
new file mode 100644
index 000000000000..aad8c27a1a3b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ split="split_two"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application android:hasCode="false" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml b/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml
new file mode 100644
index 000000000000..a367063dd43e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <public type="string" name="split_overlaid" id="0x7f040001" />
+ <string name="split_overlaid">Split TWO Overlaid</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt
new file mode 100644
index 000000000000..b1bdc967e68f
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.loader.DirectoryResourceLoader
+import android.content.res.loader.ResourceLoader
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import java.io.File
+
+class DirectoryResourceLoaderTest : ResourceLoaderTestBase() {
+
+ @get:Rule
+ val testName = TestName()
+
+ private lateinit var testDir: File
+ private lateinit var loader: ResourceLoader
+
+ @Before
+ fun setUpTestDir() {
+ testDir = context.filesDir.resolve("DirectoryResourceLoaderTest_${testName.methodName}")
+ loader = DirectoryResourceLoader(testDir)
+ }
+
+ @After
+ fun deleteTestFiles() {
+ testDir.deleteRecursively()
+ }
+
+ @Test
+ fun loadDrawableXml() {
+ "nonAssetDrawableOne" writeTo "res/drawable-nodpi-v4/non_asset_drawable.xml"
+ val provider = openArsc("nonAssetDrawableOne")
+
+ fun getValue() = (resources.getDrawable(R.drawable.non_asset_drawable) as ColorDrawable)
+ .color
+
+ assertThat(getValue()).isEqualTo(Color.parseColor("#B2D2F2"))
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo(Color.parseColor("#A3C3E3"))
+ }
+
+ @Test
+ fun loadDrawableBitmap() {
+ "nonAssetBitmapGreen" writeTo "res/drawable-nodpi-v4/non_asset_bitmap.png"
+ val provider = openArsc("nonAssetBitmapGreen")
+
+ fun getValue() = (resources.getDrawable(R.drawable.non_asset_bitmap) as BitmapDrawable)
+ .bitmap.getColor(0, 0).toArgb()
+
+ assertThat(getValue()).isEqualTo(Color.RED)
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo(Color.GREEN)
+ }
+
+ @Test
+ fun loadXml() {
+ "layoutOne" writeTo "res/layout/layout.xml"
+ val provider = openArsc("layoutOne")
+
+ fun getValue() = resources.getLayout(R.layout.layout).advanceToRoot().name
+
+ assertThat(getValue()).isEqualTo("FrameLayout")
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo("RelativeLayout")
+ }
+
+ private infix fun String.writeTo(path: String) {
+ val testFile = testDir.resolve(path)
+ testFile.parentFile!!.mkdirs()
+ resources.openRawResource(rawFile(this))
+ .copyTo(testFile.outputStream())
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt
new file mode 100644
index 000000000000..a6a83789c082
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.AssetManager
+import android.content.res.loader.DirectoryResourceLoader
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.IOException
+import java.nio.file.Paths
+
+@RunWith(Parameterized::class)
+class ResourceLoaderAssetTest : ResourceLoaderTestBase() {
+
+ companion object {
+ private const val BASE_TEST_PATH = "android/content/res/loader/test/file.txt"
+ private const val TEST_TEXT = "some text"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): Array<Array<out Any?>> {
+ val fromInputStream: ResourceLoader.(String) -> Any? = {
+ loadAsset(eq(it), anyInt())
+ }
+
+ val fromFileDescriptor: ResourceLoader.(String) -> Any? = {
+ loadAssetFd(eq(it))
+ }
+
+ val openAsset: AssetManager.() -> String? = {
+ open(BASE_TEST_PATH).reader().readText()
+ }
+
+ val openNonAsset: AssetManager.() -> String? = {
+ openNonAssetFd(BASE_TEST_PATH).readText()
+ }
+
+ return arrayOf(
+ arrayOf("assets", fromInputStream, openAsset),
+ arrayOf("", fromFileDescriptor, openNonAsset)
+ )
+ }
+ }
+
+ @get:Rule
+ val testName = TestName()
+
+ @JvmField
+ @field:Parameterized.Parameter(0)
+ var prefix: String? = null
+
+ @field:Parameterized.Parameter(1)
+ lateinit var loadAssetFunction: ResourceLoader.(String) -> Any?
+
+ @field:Parameterized.Parameter(2)
+ lateinit var openAssetFunction: AssetManager.() -> String?
+
+ private val testPath: String
+ get() = Paths.get(prefix.orEmpty(), BASE_TEST_PATH).toString()
+
+ private fun ResourceLoader.loadAsset() = loadAssetFunction(testPath)
+
+ private fun AssetManager.openAsset() = openAssetFunction()
+
+ private lateinit var testDir: File
+
+ @Before
+ fun setUpTestDir() {
+ testDir = context.filesDir.resolve("DirectoryResourceLoaderTest_${testName.methodName}")
+ testDir.resolve(testPath).apply { parentFile!!.mkdirs() }.writeText(TEST_TEXT)
+ }
+
+ @Test
+ fun multipleLoadersSearchesBackwards() {
+ // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
+ val loader = DirectoryResourceLoader(testDir)
+ val loaderWrapper = mock(ResourceLoader::class.java).apply {
+ doAnswer { loader.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
+ .`when`(this).loadAsset(anyString(), anyInt())
+ doAnswer { loader.loadAssetFd(it.arguments[0] as String) }
+ .`when`(this).loadAssetFd(anyString())
+ }
+
+ val one = loaderWrapper to ResourcesProvider.empty()
+ val two = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ inOrder(two.first, one.first).apply {
+ verify(two.first).loadAsset()
+ verify(one.first).loadAsset()
+ }
+ }
+
+ @Test(expected = FileNotFoundException::class)
+ fun failToFindThrowsFileNotFound() {
+ val one = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+ val two = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ @Test
+ fun throwingIOExceptionIsSkipped() {
+ val one = DirectoryResourceLoader(testDir) to ResourcesProvider.empty()
+ val two = mockLoader {
+ doAnswer { throw IOException() }.`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun throwingNonIOExceptionCausesFailure() {
+ val one = DirectoryResourceLoader(testDir) to ResourcesProvider.empty()
+ val two = mockLoader {
+ doAnswer { throw IllegalStateException() }.`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ private fun assertOpenedAsset() {
+ assertThat(resources.assets.openAsset()).isEqualTo(TEST_TEXT)
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
new file mode 100644
index 000000000000..e01e254b1f16
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.app.Activity
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Bundle
+import android.os.ParcelFileDescriptor
+import android.widget.FrameLayout
+import androidx.test.InstrumentationRegistry
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
+import androidx.test.runner.lifecycle.Stage
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.Arrays
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import java.util.concurrent.FutureTask
+import java.util.concurrent.TimeUnit
+
+// @Ignore("UiAutomation is crashing with not connected, not sure why")
+@RunWith(Parameterized::class)
+class ResourceLoaderChangesTest : ResourceLoaderTestBase() {
+
+ companion object {
+ private const val TIMEOUT = 30L
+ private const val OVERLAY_PACKAGE = "android.content.res.loader.test.overlay"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @get:Rule
+ val activityRule: ActivityTestRule<TestActivity> =
+ ActivityTestRule<TestActivity>(TestActivity::class.java, false, true)
+
+ // Redirect to the Activity's resources
+ override val resources: Resources
+ get() = activityRule.getActivity().resources
+
+ private val activity: TestActivity
+ get() = activityRule.getActivity()
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ @Before
+ @After
+ fun disableOverlay() {
+// enableOverlay(OVERLAY_PACKAGE, false)
+ }
+
+ @Test
+ fun activityRecreate() = verifySameBeforeAndAfter {
+ val oldActivity = activity
+ var newActivity: Activity? = null
+ instrumentation.runOnMainSync { oldActivity.recreate() }
+ instrumentation.waitForIdleSync()
+ instrumentation.runOnMainSync {
+ newActivity = ActivityLifecycleMonitorRegistry.getInstance()
+ .getActivitiesInStage(Stage.RESUMED)
+ .single()
+ }
+
+ assertThat(newActivity).isNotNull()
+ assertThat(newActivity).isNotSameAs(oldActivity)
+
+ // Return the new resources to assert on
+ return@verifySameBeforeAndAfter newActivity!!.resources
+ }
+
+ @Test
+ fun activityHandledOrientationChange() = verifySameBeforeAndAfter {
+ val latch = CountDownLatch(1)
+ val oldConfig = Configuration().apply { setTo(resources.configuration) }
+ var changedConfig: Configuration? = null
+
+ activity.callback = object : TestActivity.Callback {
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ changedConfig = newConfig
+ latch.countDown()
+ }
+ }
+
+ val isPortrait = resources.displayMetrics.run { widthPixels < heightPixels }
+ val newRotation = if (isPortrait) {
+ UiAutomation.ROTATION_FREEZE_90
+ } else {
+ UiAutomation.ROTATION_FREEZE_0
+ }
+
+ instrumentation.uiAutomation.setRotation(newRotation)
+
+ assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).isTrue()
+ assertThat(changedConfig).isNotEqualTo(oldConfig)
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ @Test
+ fun enableOverlayCausingPathChange() = verifySameBeforeAndAfter {
+ assertThat(getString(R.string.loader_path_change_test)).isEqualTo("Not overlaid")
+
+ enableOverlay(OVERLAY_PACKAGE, true)
+
+ assertThat(getString(R.string.loader_path_change_test)).isEqualTo("Overlaid")
+
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ @Test
+ fun enableOverlayChildContextUnaffected() {
+ val childContext = activity.createConfigurationContext(Configuration())
+ val childResources = childContext.resources
+ val originalValue = childResources.getString(android.R.string.cancel)
+ assertThat(childResources.getString(R.string.loader_path_change_test))
+ .isEqualTo("Not overlaid")
+
+ verifySameBeforeAndAfter {
+ enableOverlay(OVERLAY_PACKAGE, true)
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ // Loader not applied, but overlay change propagated
+ assertThat(childResources.getString(android.R.string.cancel)).isEqualTo(originalValue)
+ assertThat(childResources.getString(R.string.loader_path_change_test))
+ .isEqualTo("Overlaid")
+ }
+
+ // All these tests assert for the exact same loaders/values, so extract that logic out
+ private fun verifySameBeforeAndAfter(block: () -> Resources) {
+ // TODO(chiuwinson): atest doesn't work with @Ignore, UiAutomation not connected error
+ Assume.assumeFalse(true)
+
+ val originalValue = resources.getString(android.R.string.cancel)
+
+ val loader = "stringOne".openLoader()
+ addLoader(loader)
+
+ val oldLoaders = resources.loaders
+ val oldValue = resources.getString(android.R.string.cancel)
+
+ assertThat(oldValue).isNotEqualTo(originalValue)
+
+ val newResources = block()
+
+ val newLoaders = newResources.loaders
+ val newValue = newResources.getString(android.R.string.cancel)
+
+ assertThat(newValue).isEqualTo(oldValue)
+ assertThat(newLoaders).isEqualTo(oldLoaders)
+ }
+
+ // Copied from overlaytests LocalOverlayManager
+ private fun enableOverlay(packageName: String, enable: Boolean) {
+ val executor = Executor { Thread(it).start() }
+ val pattern = (if (enable) "[x]" else "[ ]") + " " + packageName
+ if (executeShellCommand("cmd overlay list").contains(pattern)) {
+ // nothing to do, overlay already in the requested state
+ return
+ }
+
+ val oldApkPaths = resources.assets.apkPaths
+ val task = FutureTask {
+ while (true) {
+ if (!Arrays.equals(oldApkPaths, resources.assets.apkPaths)) {
+ return@FutureTask true
+ }
+ Thread.sleep(10)
+ }
+
+ @Suppress("UNREACHABLE_CODE")
+ return@FutureTask false
+ }
+
+ val command = if (enable) "enable" else "disable"
+ executeShellCommand("cmd overlay $command $packageName")
+ executor.execute(task)
+ assertThat(task.get(TIMEOUT, TimeUnit.SECONDS)).isTrue()
+ }
+
+ private fun executeShellCommand(command: String): String {
+ val uiAutomation = instrumentation.uiAutomation
+ val pfd = uiAutomation.executeShellCommand(command)
+ return ParcelFileDescriptor.AutoCloseInputStream(pfd).use { it.reader().readText() }
+ }
+}
+
+class TestActivity : Activity() {
+
+ var callback: Callback? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(FrameLayout(this).apply {
+ setBackgroundColor(Color.BLUE)
+ })
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ callback?.onConfigurationChanged(newConfig)
+ }
+
+ interface Callback {
+ fun onConfigurationChanged(newConfig: Configuration)
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt
new file mode 100644
index 000000000000..09fd27e02b59
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.Resources
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.hamcrest.CoreMatchers.not
+import org.junit.Assume.assumeThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(Parameterized::class)
+class ResourceLoaderDrawableTest : ResourceLoaderTestBase() {
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @Test
+ fun matchingConfig() {
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ `when`(loader.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+
+ addLoader(loader)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+
+ loader.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotEqualTo(original)
+ assertThat(drawable).isInstanceOf(ColorDrawable::class.java)
+ assertThat((drawable as ColorDrawable).color).isEqualTo(Color.BLUE)
+ }
+
+ @Test
+ fun worseConfig() {
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ addLoader(loader)
+
+ updateConfiguration { densityDpi = 480 /* xhdpi */ }
+
+ getDrawable(android.R.drawable.ic_delete)
+
+ verify(loader.first, never()).loadDrawable(any(), anyInt(), anyInt(), any())
+ }
+
+ @Test
+ fun multipleLoaders() {
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loaderOne = "drawableMdpiWithoutFile".openLoader()
+ val loaderTwo = "drawableMdpiWithoutFile".openLoader()
+
+ `when`(loaderTwo.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotEqualTo(original)
+ assertThat(drawable).isInstanceOf(ColorDrawable::class.java)
+ assertThat((drawable as ColorDrawable).color).isEqualTo(Color.BLUE)
+ }
+
+ @Test(expected = Resources.NotFoundException::class)
+ fun multipleLoadersNoReturnWithoutFile() {
+ val loaderOne = "drawableMdpiWithoutFile".openLoader()
+ val loaderTwo = "drawableMdpiWithoutFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ try {
+ getDrawable(android.R.drawable.ic_delete)
+ } finally {
+ // We expect the call to fail because at least the loader won't resolve the overridden
+ // drawable, but we should still verify that both loaders were called before allowing
+ // the exception to propagate.
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+ }
+ }
+
+ @Test
+ fun multipleLoadersReturnWithFile() {
+ // Can't return a file if an ARSC
+ assumeThat(dataType, not(DataType.ARSC))
+
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loaderOne = "drawableMdpiWithFile".openLoader()
+ val loaderTwo = "drawableMdpiWithFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotNull()
+ assertThat(drawable).isInstanceOf(original.javaClass)
+ }
+
+ @Test
+ fun unhandledResourceIgnoresLoaders() {
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ `when`(loader.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+ addLoader(loader)
+
+ getDrawable(android.R.drawable.ic_menu_add)
+
+ loader.verifyLoadDrawableNotCalled()
+
+ getDrawable(android.R.drawable.ic_delete)
+
+ loader.verifyLoadDrawableCalled()
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadDrawableCalled() {
+ verify(first).loadDrawable(
+ argThat {
+ it.density == 160 &&
+ it.resourceId == android.R.drawable.ic_delete &&
+ it.string == "res/drawable-mdpi-v4/ic_delete.png"
+ },
+ eq(android.R.drawable.ic_delete),
+ eq(0),
+ any()
+ )
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadDrawableNotCalled() {
+ verify(first, never()).loadDrawable(
+ any(),
+ anyInt(),
+ anyInt(),
+ any()
+ )
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt
new file mode 100644
index 000000000000..1ec209486c18
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.Resources
+import android.content.res.XmlResourceParser
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import com.google.common.truth.Truth.assertThat
+import org.hamcrest.CoreMatchers.not
+import org.junit.Assume.assumeThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(Parameterized::class)
+class ResourceLoaderLayoutTest : ResourceLoaderTestBase() {
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @Test
+ fun singleLoader() {
+ val original = getLayout(android.R.layout.activity_list_item)
+ val mockXml = mock(XmlResourceParser::class.java)
+ val loader = "layoutWithoutFile".openLoader()
+ `when`(loader.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+
+ addLoader(loader)
+
+ val layout = getLayout(android.R.layout.activity_list_item)
+ loader.verifyLoadLayoutCalled()
+
+ assertThat(layout).isNotEqualTo(original)
+ assertThat(layout).isSameAs(mockXml)
+ }
+
+ @Test
+ fun multipleLoaders() {
+ val original = getLayout(android.R.layout.activity_list_item)
+ val loaderOne = "layoutWithoutFile".openLoader()
+ val loaderTwo = "layoutWithoutFile".openLoader()
+
+ val mockXml = mock(XmlResourceParser::class.java)
+ `when`(loaderTwo.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+
+ addLoader(loaderOne, loaderTwo)
+
+ val layout = getLayout(android.R.layout.activity_list_item)
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+
+ assertThat(layout).isNotEqualTo(original)
+ assertThat(layout).isSameAs(mockXml)
+ }
+
+ @Test(expected = Resources.NotFoundException::class)
+ fun multipleLoadersNoReturnWithoutFile() {
+ val loaderOne = "layoutWithoutFile".openLoader()
+ val loaderTwo = "layoutWithoutFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ try {
+ getLayout(android.R.layout.activity_list_item)
+ } finally {
+ // We expect the call to fail because at least one loader must resolve the overridden
+ // layout, but we should still verify that both loaders were called before allowing
+ // the exception to propagate.
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+ }
+ }
+
+ @Test
+ fun multipleLoadersReturnWithFile() {
+ // Can't return a file if an ARSC
+ assumeThat(dataType, not(DataType.ARSC))
+
+ val loaderOne = "layoutWithFile".openLoader()
+ val loaderTwo = "layoutWithFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ val xml = getLayout(android.R.layout.activity_list_item)
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+
+ assertThat(xml).isNotNull()
+ }
+
+ @Test
+ fun unhandledResourceIgnoresLoaders() {
+ val loader = "layoutWithoutFile".openLoader()
+ val mockXml = mock(XmlResourceParser::class.java)
+ `when`(loader.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+ addLoader(loader)
+
+ getLayout(android.R.layout.preference_category)
+
+ verify(loader.first, never())
+ .loadXmlResourceParser(anyString(), anyInt())
+
+ getLayout(android.R.layout.activity_list_item)
+
+ loader.verifyLoadLayoutCalled()
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadLayoutCalled() {
+ verify(first).loadXmlResourceParser(
+ "res/layout/activity_list_item.xml",
+ android.R.layout.activity_list_item
+ )
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadLayoutNotCalled() {
+ verify(first, never()).loadXmlResourceParser(
+ anyString(),
+ anyInt()
+ )
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
new file mode 100644
index 000000000000..5af453d526e4
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.Context
+import android.content.res.AssetManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import android.os.ParcelFileDescriptor
+import android.util.TypedValue
+import androidx.annotation.DimenRes
+import androidx.annotation.DrawableRes
+import androidx.annotation.LayoutRes
+import androidx.annotation.StringRes
+import androidx.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Before
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import java.io.Closeable
+
+abstract class ResourceLoaderTestBase {
+
+ open lateinit var dataType: DataType
+
+ protected lateinit var context: Context
+ protected open val resources: Resources
+ get() = context.resources
+ protected open val assets: AssetManager
+ get() = resources.assets
+
+ // Track opened streams and ResourcesProviders to close them after testing
+ private val openedObjects = mutableListOf<Closeable>()
+
+ @Before
+ fun setUpBase() {
+ context = InstrumentationRegistry.getTargetContext()
+ }
+
+ @After
+ fun removeAllLoaders() {
+ resources.setLoaders(null)
+ openedObjects.forEach {
+ try {
+ it.close()
+ } catch (ignored: Exception) {
+ }
+ }
+ }
+
+ protected fun getString(@StringRes stringRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getString(stringRes) }
+
+ protected fun getDrawable(@DrawableRes drawableRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getDrawable(drawableRes) }
+
+ protected fun getLayout(@LayoutRes layoutRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getLayout(layoutRes) }
+
+ protected fun getDimensionPixelSize(@DimenRes dimenRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getDimensionPixelSize(dimenRes) }
+
+ private fun <T> logResolution(debugLog: Boolean = false, block: Resources.() -> T): T {
+ if (debugLog) {
+ resources.assets.setResourceResolutionLoggingEnabled(true)
+ }
+
+ var thrown = false
+
+ try {
+ return resources.block()
+ } catch (t: Throwable) {
+ // No good way to log to test output other than throwing an exception
+ if (debugLog) {
+ thrown = true
+ throw IllegalStateException(resources.assets.lastResourceResolution, t)
+ } else {
+ throw t
+ }
+ } finally {
+ if (!thrown && debugLog) {
+ throw IllegalStateException(resources.assets.lastResourceResolution)
+ }
+ }
+ }
+
+ protected fun updateConfiguration(block: Configuration.() -> Unit) {
+ val configuration = Configuration().apply {
+ setTo(resources.configuration)
+ block()
+ }
+
+ resources.updateConfiguration(configuration, resources.displayMetrics)
+ }
+
+ protected fun String.openLoader(
+ dataType: DataType = this@ResourceLoaderTestBase.dataType
+ ): Pair<ResourceLoader, ResourcesProvider> = when (dataType) {
+ DataType.APK -> {
+ mock(ResourceLoader::class.java) to context.copiedRawFile("${this}Apk").use {
+ ResourcesProvider.loadFromApk(it)
+ }.also { openedObjects += it }
+ }
+ DataType.ARSC -> {
+ mock(ResourceLoader::class.java) to openArsc(this)
+ }
+ DataType.SPLIT -> {
+ mock(ResourceLoader::class.java) to ResourcesProvider.loadFromSplit(context, this)
+ }
+ DataType.ASSET -> mockLoader {
+ doAnswer { byteInputStream() }.`when`(it)
+ .loadAsset(eq("assets/Asset.txt"), anyInt())
+ }
+ DataType.ASSET_FD -> mockLoader {
+ doAnswer {
+ val file = context.filesDir.resolve("Asset.txt")
+ file.writeText(this)
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ }.`when`(it).loadAssetFd("assets/Asset.txt")
+ }
+ DataType.NON_ASSET -> mockLoader {
+ doAnswer {
+ val file = context.filesDir.resolve("NonAsset.txt")
+ file.writeText(this)
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ }.`when`(it).loadAssetFd("NonAsset.txt")
+ }
+ DataType.NON_ASSET_DRAWABLE -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it).loadDrawable(argThat { value ->
+ value.type == TypedValue.TYPE_STRING &&
+ value.resourceId == 0x7f010001 &&
+ value.string == "res/drawable-nodpi-v4/non_asset_drawable.xml"
+ }, eq(0x7f010001), anyInt(), ArgumentMatchers.any())
+
+ doAnswer { context.copiedRawFile(this) }.`when`(it)
+ .loadAssetFd("res/drawable-nodpi-v4/non_asset_drawable.xml")
+ }
+ DataType.NON_ASSET_BITMAP -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it).loadDrawable(argThat { value ->
+ value.type == TypedValue.TYPE_STRING &&
+ value.resourceId == 0x7f010000 &&
+ value.string == "res/drawable-nodpi-v4/non_asset_bitmap.png"
+ }, eq(0x7f010000), anyInt(), ArgumentMatchers.any())
+
+ doAnswer { resources.openRawResourceFd(rawFile(this)).createInputStream() }
+ .`when`(it)
+ .loadAsset(eq("res/drawable-nodpi-v4/non_asset_bitmap.png"), anyInt())
+ }
+ DataType.NON_ASSET_LAYOUT -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it)
+ .loadXmlResourceParser("res/layout/layout.xml", 0x7f020000)
+
+ doAnswer { context.copiedRawFile(this) }.`when`(it)
+ .loadAssetFd("res/layout/layout.xml")
+ }
+ }
+
+ protected fun mockLoader(
+ provider: ResourcesProvider = ResourcesProvider.empty(),
+ block: (ResourceLoader) -> Unit = {}
+ ): Pair<ResourceLoader, ResourcesProvider> {
+ return mock(ResourceLoader::class.java, Utils.ANSWER_THROWS)
+ .apply(block) to provider
+ }
+
+ protected fun openArsc(rawName: String): ResourcesProvider {
+ return context.copiedRawFile("${rawName}Arsc")
+ .use { ResourcesProvider.loadFromArsc(it) }
+ .also { openedObjects += it }
+ }
+
+ // This specifically uses addLoader so both behaviors are tested
+ protected fun addLoader(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ pairs.forEach { resources.addLoader(it.first, it.second) }
+ }
+
+ protected fun setLoaders(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ resources.setLoaders(pairs.map { android.util.Pair(it.first, it.second) })
+ }
+
+ protected fun addLoader(pair: Pair<out ResourceLoader, ResourcesProvider>, index: Int) {
+ resources.addLoader(pair.first, pair.second, index)
+ }
+
+ protected fun removeLoader(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ pairs.forEach { resources.removeLoader(it.first) }
+ }
+
+ protected fun getLoaders(): MutableList<Pair<ResourceLoader, ResourcesProvider>> {
+ // Cast instead of toMutableList to maintain the same object
+ return resources.getLoaders() as MutableList<Pair<ResourceLoader, ResourcesProvider>>
+ }
+
+ enum class DataType {
+ APK,
+ ARSC,
+ SPLIT,
+ ASSET,
+ ASSET_FD,
+ NON_ASSET,
+ NON_ASSET_DRAWABLE,
+ NON_ASSET_BITMAP,
+ NON_ASSET_LAYOUT,
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
new file mode 100644
index 000000000000..017552a02152
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Tests generic ResourceLoader behavior. Intentionally abstract in its test methodology because
+ * the behavior being verified isn't specific to any resource type. As long as it can pass an
+ * equals check.
+ *
+ * Currently tests strings and dimens since String and any Number seemed most relevant to verify.
+ */
+@RunWith(Parameterized::class)
+class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
+
+ companion object {
+ @Parameterized.Parameters(name = "{1} {0}")
+ @JvmStatic
+ fun parameters(): Array<Any> {
+ val parameters = mutableListOf<Parameter>()
+
+ // R.string
+ parameters += Parameter(
+ { getString(android.R.string.cancel) },
+ "stringOne", { "SomeRidiculouslyUnlikelyStringOne" },
+ "stringTwo", { "SomeRidiculouslyUnlikelyStringTwo" },
+ listOf(DataType.APK, DataType.ARSC)
+ )
+
+ // R.dimen
+ parameters += Parameter(
+ { resources.getDimensionPixelSize(android.R.dimen.app_icon_size) },
+ "dimenOne", { 564716.dpToPx(resources) },
+ "dimenTwo", { 565717.dpToPx(resources) },
+ listOf(DataType.APK, DataType.ARSC)
+ )
+
+ // File in the assets directory
+ parameters += Parameter(
+ { assets.open("Asset.txt").reader().readText() },
+ "assetOne", { "assetOne" },
+ "assetTwo", { "assetTwo" },
+ listOf(DataType.ASSET)
+ )
+
+ // From assets directory returning file descriptor
+ parameters += Parameter(
+ { assets.openFd("Asset.txt").readText() },
+ "assetOne", { "assetOne" },
+ "assetTwo", { "assetTwo" },
+ listOf(DataType.ASSET_FD)
+ )
+
+ // From root directory returning file descriptor
+ parameters += Parameter(
+ { assets.openNonAssetFd("NonAsset.txt").readText() },
+ "NonAssetOne", { "NonAssetOne" },
+ "NonAssetTwo", { "NonAssetTwo" },
+ listOf(DataType.NON_ASSET)
+ )
+
+ // Asset as compiled XML drawable
+ parameters += Parameter(
+ { (getDrawable(R.drawable.non_asset_drawable) as ColorDrawable).color },
+ "nonAssetDrawableOne", { Color.parseColor("#A3C3E3") },
+ "nonAssetDrawableTwo", { Color.parseColor("#3A3C3E") },
+ listOf(DataType.NON_ASSET_DRAWABLE)
+ )
+
+ // Asset as compiled bitmap drawable
+ parameters += Parameter(
+ {
+ (getDrawable(R.drawable.non_asset_bitmap) as BitmapDrawable)
+ .bitmap.getColor(0, 0).toArgb()
+ },
+ "nonAssetBitmapGreen", { Color.GREEN },
+ "nonAssetBitmapBlue", { Color.BLUE },
+ listOf(DataType.NON_ASSET_BITMAP)
+ )
+
+ // Asset as compiled XML layout
+ parameters += Parameter(
+ { getLayout(R.layout.layout).advanceToRoot().name },
+ "layoutOne", { "RelativeLayout" },
+ "layoutTwo", { "LinearLayout" },
+ listOf(DataType.NON_ASSET_LAYOUT)
+ )
+
+ // Isolated resource split
+ parameters += Parameter(
+ { getString(R.string.split_overlaid) },
+ "split_one", { "Split ONE Overlaid" },
+ "split_two", { "Split TWO Overlaid" },
+ listOf(DataType.SPLIT)
+ )
+
+ return parameters.flatMap { parameter ->
+ parameter.dataTypes.map { dataType ->
+ arrayOf(dataType, parameter)
+ }
+ }.toTypedArray()
+ }
+ }
+
+ @Suppress("LateinitVarOverridesLateinitVar")
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @field:Parameterized.Parameter(1)
+ lateinit var parameter: Parameter
+
+ private val valueOne by lazy { parameter.valueOne(this) }
+ private val valueTwo by lazy { parameter.valueTwo(this) }
+
+ private fun openOne() = parameter.loaderOne.openLoader()
+ private fun openTwo() = parameter.loaderTwo.openLoader()
+
+ // Class method for syntax highlighting purposes
+ private fun getValue() = parameter.getValue(this)
+
+ @Test
+ fun verifyValueUniqueness() {
+ // Ensure the parameters are valid in case of coding errors
+ assertNotEquals(valueOne, getValue())
+ assertNotEquals(valueTwo, getValue())
+ assertNotEquals(valueOne, valueTwo)
+ }
+
+ @Test
+ fun addMultipleLoaders() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun setMultipleLoaders() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ setLoaders(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ setLoaders()
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun getLoadersContainsAll() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertThat(getLoaders()).containsAllOf(testOne, testTwo)
+ }
+
+ @Test
+ fun getLoadersDoesNotLeakMutability() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ val loaders = getLoaders()
+ loaders += testTwo
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun alreadyAddedThrows() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+ addLoader(testTwo)
+ addLoader(testOne)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun alreadyAddedAndSetThrows() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+ addLoader(testTwo)
+ setLoaders(testTwo)
+ }
+
+ @Test
+ fun repeatedRemoveSucceeds() {
+ val originalValue = getValue()
+ val testOne = openOne()
+
+ addLoader(testOne)
+
+ assertNotEquals(originalValue, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun addToFront() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 0)
+
+ assertEquals(valueOne, getValue())
+
+ // Remove top loader, so previously added to front should now resolve
+ removeLoader(testOne)
+ assertEquals(valueTwo, getValue())
+ }
+
+ @Test
+ fun addToEnd() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 1)
+
+ assertEquals(valueTwo, getValue())
+ }
+
+ @Test(expected = IndexOutOfBoundsException::class)
+ fun addPastEnd() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 2)
+ }
+
+ @Test(expected = IndexOutOfBoundsException::class)
+ fun addBeforeFront() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, -1)
+ }
+
+ @Test
+ fun reorder() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(valueTwo, getValue())
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ data class Parameter(
+ val getValue: ResourceLoaderValuesTest.() -> Any,
+ val loaderOne: String,
+ val valueOne: ResourceLoaderValuesTest.() -> Any,
+ val loaderTwo: String,
+ val valueTwo: ResourceLoaderValuesTest.() -> Any,
+ val dataTypes: List<DataType>
+ ) {
+ override fun toString(): String {
+ val prefix = loaderOne.commonPrefixWith(loaderTwo)
+ return "$prefix${loaderOne.removePrefix(prefix)}|${loaderTwo.removePrefix(prefix)}"
+ }
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
new file mode 100644
index 000000000000..df2d09adf503
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.content.Context
+import android.content.res.AssetFileDescriptor
+import android.content.res.Resources
+import android.os.ParcelFileDescriptor
+import android.util.TypedValue
+import org.mockito.Answers
+import org.mockito.stubbing.Answer
+import org.xmlpull.v1.XmlPullParser
+import java.io.File
+
+// Enforce use of [android.util.Pair] instead of Kotlin's so it matches the ResourceLoader APIs
+typealias Pair<F, S> = android.util.Pair<F, S>
+infix fun <A, B> A.to(that: B): Pair<A, B> = Pair.create(this, that)!!
+
+object Utils {
+ val ANSWER_THROWS = Answer<Any> {
+ when (val name = it.method.name) {
+ "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+ else -> throw UnsupportedOperationException("$name with " +
+ "${it.arguments?.joinToString()} should not be called")
+ }
+ }
+}
+
+fun Int.dpToPx(resources: Resources) = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ this.toFloat(),
+ resources.displayMetrics
+).toInt()
+
+fun AssetFileDescriptor.readText() = createInputStream().reader().readText()
+
+fun rawFile(fileName: String) = R.raw::class.java.getDeclaredField(fileName).getInt(null)
+
+fun XmlPullParser.advanceToRoot() = apply {
+ while (next() != XmlPullParser.START_TAG) {
+ // Empty
+ }
+}
+
+fun Context.copiedRawFile(fileName: String): ParcelFileDescriptor {
+ return resources.openRawResourceFd(rawFile(fileName)).use { asset ->
+ // AssetManager doesn't expose a direct file descriptor to the asset, so copy it to
+ // an individual file so one can be created manually.
+ val copiedFile = File(filesDir, fileName)
+ asset.createInputStream().use { input ->
+ copiedFile.outputStream().use { output ->
+ input.copyTo(output)
+ }
+ }
+
+ ParcelFileDescriptor.open(copiedFile, ParcelFileDescriptor.MODE_READ_WRITE)
+ }
+}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index d36a82684e9e..61281eea7134 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -16,17 +16,30 @@
-->
<permissions>
<privapp-permissions package="com.google.android.car.kitchensink">
+ <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+ <permission name="android.permission.ACCESS_WIFI_STATE"/>
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.INJECT_EVENTS"/>
+ <!-- use for CarServiceUnitTest and CarServiceTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <!-- use for CarServiceUnitTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.LOCATION_HARDWARE"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.REBOOT"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index f1ba3f6f7859..07a5617009d5 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -56,7 +56,7 @@ Do NOT include any apps that are updatable via Play Store!
<!-- TODO (b/141954427): Remove networkstack -->
<hidden-api-whitelisted-app package="com.android.networkstack" />
<!-- TODO (b/141954427): Remove wifistack -->
- <hidden-api-whitelisted-app package="com.android.server.wifistack" />
+ <hidden-api-whitelisted-app package="com.android.wifi" />
<hidden-api-whitelisted-app package="com.android.smspush" />
<hidden-api-whitelisted-app package="com.android.spare_parts" />
<hidden-api-whitelisted-app package="com.android.statementservice" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 51136b993e57..10f6b6925431 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -354,7 +354,7 @@ applications that come with the platform
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
</privapp-permissions>
- <privapp-permissions package="com.android.server.wifistack">
+ <privapp-permissions package="com.android.wifi">
<permission name="android.permission.CHANGE_CONFIGURATION"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DUMP"/>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
new file mode 100644
index 000000000000..8c6a9371d53b
--- /dev/null
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * @hide
+ */
+public final class BLASTBufferQueue {
+ // Note: This field is accessed by native code.
+ private long mNativeObject; // BLASTBufferQueue*
+
+ private static native long nativeCreate(long surfaceControl, long width, long height);
+ private static native void nativeDestroy(long ptr);
+ private static native Surface nativeGetSurface(long ptr);
+ private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+ private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+
+ /** Create a new connection with the surface flinger. */
+ public BLASTBufferQueue(SurfaceControl sc, int width, int height) {
+ mNativeObject = nativeCreate(sc.mNativeObject, width, height);
+ }
+
+ public void destroy() {
+ nativeDestroy(mNativeObject);
+ }
+
+ public Surface getSurface() {
+ return nativeGetSurface(mNativeObject);
+ }
+
+ public void setNextTransaction(SurfaceControl.Transaction t) {
+ nativeSetNextTransaction(mNativeObject, t.mNativeObject);
+ }
+
+ public void update(SurfaceControl sc, int width, int height) {
+ nativeUpdate(mNativeObject, sc.mNativeObject, width, height);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mNativeObject != 0) {
+ nativeDestroy(mNativeObject);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+}
+
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 96ac0f9b38b5..c6586ecfceb9 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -97,6 +97,14 @@ import java.lang.annotation.RetentionPolicy;
* @attr ref android.R.styleable#GradientDrawablePadding_bottom
*/
public class GradientDrawable extends Drawable {
+
+ /**
+ * Flag to determine if we should wrap negative gradient angle measurements
+ * for API levels that support it
+ * @hide
+ */
+ public static boolean sWrapNegativeAngleMeasurements = true;
+
/**
* Shape is a rectangle, possibly with rounded corners
*/
@@ -151,6 +159,9 @@ public class GradientDrawable extends Drawable {
/** Radius is a fraction of the bounds size. */
private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
+ /** Default orientation for GradientDrawable **/
+ private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP_BOTTOM;
+
/** @hide */
@IntDef({RADIUS_TYPE_PIXELS, RADIUS_TYPE_FRACTION, RADIUS_TYPE_FRACTION_PARENT})
@Retention(RetentionPolicy.SOURCE)
@@ -207,7 +218,7 @@ public class GradientDrawable extends Drawable {
}
public GradientDrawable() {
- this(new GradientState(Orientation.TOP_BOTTOM, null), null);
+ this(new GradientState(DEFAULT_ORIENTATION, null), null);
}
/**
@@ -1757,33 +1768,48 @@ public class GradientDrawable extends Drawable {
}
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
- st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
- switch (st.mAngle) {
- case 0:
- st.mOrientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- st.mOrientation = Orientation.BL_TR;
- break;
- case 90:
- st.mOrientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- st.mOrientation = Orientation.BR_TL;
- break;
- case 180:
- st.mOrientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- st.mOrientation = Orientation.TR_BL;
- break;
- case 270:
- st.mOrientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- st.mOrientation = Orientation.TL_BR;
- break;
+ // GradientDrawable historically has not parsed negative angle measurements and always
+ // stays on the default orientation for API levels older than Q.
+ // Only configure the orientation if the angle is greater than zero.
+ // Otherwise fallback on Orientation.TOP_BOTTOM
+ // In Android Q and later, actually wrap the negative angle measurement to the correct
+ // value
+ if (sWrapNegativeAngleMeasurements) {
+ st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+ } else {
+ st.mAngle = angle % 360;
+ }
+
+ if (st.mAngle >= 0) {
+ switch (st.mAngle) {
+ case 0:
+ st.mOrientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ st.mOrientation = Orientation.BL_TR;
+ break;
+ case 90:
+ st.mOrientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ st.mOrientation = Orientation.BR_TL;
+ break;
+ case 180:
+ st.mOrientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ st.mOrientation = Orientation.TR_BL;
+ break;
+ case 270:
+ st.mOrientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ st.mOrientation = Orientation.TL_BR;
+ break;
+ }
+ } else {
+ st.mOrientation = DEFAULT_ORIENTATION;
}
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index eeaefc5b157c..a34a6c0b3724 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -166,7 +166,10 @@ cc_test {
static_libs: common_test_libs + ["liblog", "libz"],
},
},
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ "tests/data/**/*.arsc",
+ ],
test_suites: ["device-tests"],
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index cf2ef3070385..b309621435b5 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -42,12 +42,16 @@ static const std::string kResourcesArsc("resources.arsc");
ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
- time_t last_mod_time)
- : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) {
+ time_t last_mod_time,
+ bool for_loader)
+ : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
+ for_loader(for_loader) {
}
-std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
+ bool for_loader) {
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/,
+ for_loader);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
@@ -76,9 +80,21 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
const std::string& friendly_name,
- bool system, bool force_shared_lib) {
+ bool system, bool force_shared_lib,
+ bool for_loader) {
return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- system, force_shared_lib);
+ system, force_shared_lib, for_loader);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
+ bool for_loader) {
+ return LoadArscImpl({} /*fd*/, path, for_loader);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
+ const std::string& friendly_name,
+ bool for_loader) {
+ return LoadArscImpl(std::move(fd), friendly_name, for_loader);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
@@ -104,7 +120,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
+ std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library,
+ bool for_loader) {
::ZipArchiveHandle unmanaged_handle;
int32_t result;
if (fd >= 0) {
@@ -123,7 +140,8 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
time_t last_mod_time = getFileModDate(path.c_str());
// Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time));
+ std::unique_ptr<ApkAssets>
+ loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader));
// Find the resource table.
::ZipEntry entry;
@@ -152,7 +170,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library);
+ LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library, for_loader);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -162,8 +180,53 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
return std::move(loaded_apk);
}
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
+ const std::string& path,
+ bool for_loader) {
+ std::unique_ptr<Asset> resources_asset;
+
+ if (fd >= 0) {
+ resources_asset = std::unique_ptr<Asset>(Asset::createFromFd(fd.release(), nullptr,
+ Asset::AccessMode::ACCESS_BUFFER));
+ } else {
+ resources_asset = CreateAssetFromFile(path);
+ }
+
+ if (resources_asset == nullptr) {
+ LOG(ERROR) << "Failed to open ARSC '" << path;
+ return {};
+ }
+
+ time_t last_mod_time = getFileModDate(path.c_str());
+
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader));
+ loaded_apk->resources_asset_ = std::move(resources_asset);
+
+ const StringPiece data(
+ reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
+ loaded_apk->resources_asset_->getLength());
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader);
+ if (loaded_apk->loaded_arsc_ == nullptr) {
+ LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
+ return {};
+ }
+
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(bool for_loader) {
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, "", -1, for_loader));
+ loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
+}
+
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
- CHECK(zip_handle_ != nullptr);
+ // If this is a resource loader from an .arsc, there will be no zip handle
+ if (zip_handle_ == nullptr) {
+ return {};
+ }
::ZipEntry entry;
int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
@@ -205,7 +268,10 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod
bool ApkAssets::ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& f) const {
- CHECK(zip_handle_ != nullptr);
+ // If this is a resource loader from an .arsc, there will be no zip handle
+ if (zip_handle_ == nullptr) {
+ return false;
+ }
std::string root_path_full = root_path;
if (root_path_full.back() != '/') {
@@ -252,6 +318,11 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
}
bool ApkAssets::IsUpToDate() const {
+ // Loaders are invalidated by the app, not the system, so assume up to date
+ if (for_loader) {
+ return true;
+ }
+
return last_mod_time_ == getFileModDate(path_.c_str());
}
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 92125c9da8bb..c132f343713f 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -133,14 +133,24 @@ Asset::Asset(void)
*/
/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
{
+ return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode);
+}
+
+/*
+ * Create a new Asset from a file on disk. There is a fair chance that
+ * the file doesn't actually exist.
+ *
+ * We can use "mode" to decide how we want to go about it.
+ */
+/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode)
+{
+ if (fd < 0) {
+ return NULL;
+ }
+
_FileAsset* pAsset;
status_t result;
off64_t length;
- int fd;
-
- fd = open(fileName, O_RDONLY | O_BINARY);
- if (fd < 0)
- return NULL;
/*
* Under Linux, the lseek fails if we actually opened a directory. To
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index eec49df79630..e914f37bcac4 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -493,8 +493,12 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
- // If the package is an overlay, then even configurations that are the same MUST be chosen.
+
+ // If the package is an overlay or custom loader,
+ // then even configurations that are the same MUST be chosen.
const bool package_is_overlay = loaded_package->IsOverlay();
+ const bool package_is_loader = loaded_package->IsCustomLoader();
+ const bool should_overlay = package_is_overlay || package_is_loader;
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
@@ -508,10 +512,28 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
} else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::BETTER_MATCH;
+ }
+ } else if (should_overlay && this_config.compare(*best_config) == 0) {
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
+ } else if (package_is_overlay) {
+ resolution_type = Resolution::Step::Type::OVERLAID;
+ }
} else {
+ if (resource_resolution_logging_enabled_) {
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::SKIPPED_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::SKIPPED;
+ }
+ resolution_steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
@@ -520,6 +542,16 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
const ResTable_type* type = filtered_group.types[i];
const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
+ if (resource_resolution_logging_enabled_) {
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::NO_ENTRY;
+ }
+ resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
@@ -554,9 +586,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
} else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::BETTER_MATCH;
+ }
+ } else if (should_overlay && this_config.compare(*best_config) == 0) {
+ if (package_is_overlay) {
+ resolution_type = Resolution::Step::Type::OVERLAID;
+ } else if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
+ }
} else {
continue;
}
@@ -678,9 +718,27 @@ std::string AssetManager2::GetLastResourceResolution() const {
case Resolution::Step::Type::BETTER_MATCH:
prefix = "Found better";
break;
+ case Resolution::Step::Type::BETTER_MATCH_LOADER:
+ prefix = "Found better in loader";
+ break;
case Resolution::Step::Type::OVERLAID:
prefix = "Overlaid";
break;
+ case Resolution::Step::Type::OVERLAID_LOADER:
+ prefix = "Overlaid by loader";
+ break;
+ case Resolution::Step::Type::SKIPPED:
+ prefix = "Skipped";
+ break;
+ case Resolution::Step::Type::SKIPPED_LOADER:
+ prefix = "Skipped loader";
+ break;
+ case Resolution::Step::Type::NO_ENTRY:
+ prefix = "No entry";
+ break;
+ case Resolution::Step::Type::NO_ENTRY_LOADER:
+ prefix = "No entry for loader";
+ break;
}
if (!prefix.empty()) {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 72873abc6a42..882dc0d71759 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -401,7 +401,9 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
const LoadedIdmap* loaded_idmap,
- bool system, bool load_as_shared_library) {
+ bool system,
+ bool load_as_shared_library,
+ bool for_loader) {
ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
@@ -430,6 +432,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
loaded_package->overlay_ = true;
}
+ if (for_loader) {
+ loaded_package->custom_loader_ = true;
+ }
+
if (header->header.headerSize >= sizeof(ResTable_package)) {
uint32_t type_id_offset = dtohl(header->typeIdOffset);
if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
@@ -696,7 +702,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library) {
+ bool load_as_shared_library, bool for_loader) {
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -735,7 +741,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library);
+ LoadedPackage::Load(child_chunk,
+ loaded_idmap,
+ system_,
+ load_as_shared_library,
+ for_loader);
if (!loaded_package) {
return false;
}
@@ -758,9 +768,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
}
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
- const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library) {
- ATRACE_NAME("LoadedArsc::LoadTable");
+ const LoadedIdmap* loaded_idmap,
+ bool system,
+ bool load_as_shared_library,
+ bool for_loader) {
+ ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
@@ -771,7 +783,10 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
- if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) {
+ if (!loaded_arsc->LoadTable(chunk,
+ loaded_idmap,
+ load_as_shared_library,
+ for_loader)) {
return {};
}
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 535386920265..3fe2c5b0e21a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
+const void* ResStringPool::data() const
+{
+ return mHeader;
+}
+
bool ResStringPool::isSorted() const
{
return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 49fc82bff11e..625b68207d83 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -40,7 +40,8 @@ class ApkAssets {
// Creates an ApkAssets.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false);
+ static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false,
+ bool for_loader = false);
// Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library.
// If `system` is true, the package is marked as a system package, and allows some functions to
@@ -63,7 +64,21 @@ class ApkAssets {
// If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library.
static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd,
const std::string& friendly_name, bool system,
- bool force_shared_lib);
+ bool force_shared_lib,
+ bool for_loader = false);
+
+ // Creates an empty wrapper ApkAssets from the given path which points to an .arsc.
+ static std::unique_ptr<const ApkAssets> LoadArsc(const std::string& path,
+ bool for_loader = false);
+
+ // Creates an empty wrapper ApkAssets from the given file descriptor which points to an .arsc,
+ // Takes ownership of the file descriptor.
+ static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
+ const std::string& friendly_name,
+ bool resource_loader = false);
+
+ // Creates a totally empty ApkAssets with no resources table and no file entries.
+ static std::unique_ptr<const ApkAssets> LoadEmpty(bool resource_loader = false);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -86,24 +101,33 @@ class ApkAssets {
bool IsUpToDate() const;
+ // Creates an Asset from any file on the file system.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<const LoadedIdmap> loaded_idmap,
- bool system, bool load_as_shared_library);
+ bool system, bool load_as_shared_library,
+ bool resource_loader = false);
- // Creates an Asset from any file on the file system.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+ static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
+ const std::string& path,
+ bool resource_loader = false);
- ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time);
+ ApkAssets(ZipArchiveHandle unmanaged_handle,
+ const std::string& path,
+ time_t last_mod_time,
+ bool for_loader = false);
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
+ using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
time_t last_mod_time_;
+ bool for_loader;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 9d12a35395c9..053dbb7864c6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -121,6 +121,11 @@ public:
*/
const char* getAssetSource(void) const { return mAssetSource.string(); }
+ /*
+ * Create the asset from a file descriptor.
+ */
+ static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode);
+
protected:
/*
* Adds this Asset to the global Asset list for debugging and
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index de46081a6aa3..c7348b180648 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -382,7 +382,13 @@ class AssetManager2 {
enum class Type {
INITIAL,
BETTER_MATCH,
- OVERLAID
+ BETTER_MATCH_LOADER,
+ OVERLAID,
+ OVERLAID_LOADER,
+ SKIPPED,
+ SKIPPED_LOADER,
+ NO_ENTRY,
+ NO_ENTRY_LOADER,
};
// Marks what kind of override this step was.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 950f5413f550..1a56876b9686 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -137,7 +137,8 @@ class LoadedPackage {
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library);
+ bool load_as_shared_library,
+ bool load_as_custom_loader);
~LoadedPackage();
@@ -187,6 +188,11 @@ class LoadedPackage {
return overlay_;
}
+ // Returns true if this package is a custom loader and should behave like an overlay
+ inline bool IsCustomLoader() const {
+ return custom_loader_;
+ }
+
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
// package could have been assigned a different package ID than what this LoadedPackage was
// compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
@@ -260,6 +266,7 @@ class LoadedPackage {
bool dynamic_ = false;
bool system_ = false;
bool overlay_ = false;
+ bool custom_loader_ = false;
bool defines_overlayable_ = false;
ByteBucketArray<TypeSpecPtr> type_specs_;
@@ -282,7 +289,8 @@ class LoadedArsc {
static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap = nullptr,
bool system = false,
- bool load_as_shared_library = false);
+ bool load_as_shared_library = false,
+ bool for_loader = false);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -311,7 +319,19 @@ class LoadedArsc {
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library);
+ bool LoadTable(
+ const Chunk& chunk,
+ const LoadedIdmap* loaded_idmap,
+ bool load_as_shared_library,
+ bool for_loader
+ );
+
+ static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
+ const char* data,
+ size_t length,
+ const LoadedIdmap* loaded_idmap = nullptr,
+ bool load_as_shared_library = false,
+ bool for_loader = false);
ResStringPool global_string_pool_;
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fc635aaeb0d8..c8ace90e6515 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -523,6 +523,8 @@ public:
size_t size() const;
size_t styleCount() const;
size_t bytes() const;
+ const void* data() const;
+
bool isSorted() const;
bool isUTF8() const;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index d58e8d20c8aa..fd57a92c216b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -25,6 +25,7 @@
#include "data/overlayable/R.h"
#include "data/sparse/R.h"
#include "data/styles/R.h"
+#include "data/system/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
@@ -387,6 +388,39 @@ TEST(LoadedArscTest, GetOverlayableMap) {
ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable");
}
+TEST(LoadedArscTest, LoadCustomLoader) {
+ std::string contents;
+
+ std::unique_ptr<Asset>
+ asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+
+ MockLoadedIdmap loaded_idmap;
+ const StringPiece data(
+ reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
+ asset->getLength());
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(data, nullptr, false, false, true);
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(android::R::string::cancel));
+ ASSERT_THAT(package, NotNull());
+ EXPECT_THAT(package->GetPackageName(), StrEq("android"));
+ EXPECT_THAT(package->GetPackageId(), Eq(0x01));
+
+ const uint8_t type_index = get_type_id(android::R::string::cancel) - 1;
+ const uint16_t entry_index = get_entry_id(android::R::string::cancel);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+
+ const ResTable_type* type = type_spec->types[0];
+ ASSERT_THAT(type, NotNull());
+ ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+}
+
// structs with size fields (like Res_value, ResTable_entry) should be
// backwards and forwards compatible (aka checking the size field against
// sizeof(Res_value) might not be backwards compatible.
diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc
new file mode 100644
index 000000000000..2c881f2cdfe5
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/resources.arsc
Binary files differ
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index becb38830fb3..374107484784 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -40,6 +40,12 @@ struct R {
number = 0x01030000, // sv
};
};
+
+ struct string {
+ enum : uint32_t {
+ cancel = 0x01040000,
+ };
+ };
};
} // namespace android
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 9c36d76cf370..6824be8e1e3b 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,8 @@ public class Location implements Parcelable {
*
* @hide
*/
+ @TestApi
+ @SystemApi
public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
/**
@@ -1214,8 +1217,9 @@ public class Location implements Parcelable {
* @param value the Location to attach
* @hide
*/
- @UnsupportedAppUsage
- public void setExtraLocation(String key, Location value) {
+ @TestApi
+ @SystemApi
+ public void setExtraLocation(@Nullable String key, @Nullable Location value) {
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 510ee442f852..b7a9ffe93bae 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -649,7 +649,7 @@ import java.util.concurrent.locks.ReentrantLock;
are not consumed by the Surface in a timely manner). Or it may be configured to not drop excessive
frames. In the latter mode if the Surface is not consuming output frames fast enough, it will
eventually block the decoder. Prior to {@link android.os.Build.VERSION_CODES#Q} the exact behavior
- was undefined, with the exception that View surfaces (SuerfaceView or TextureView) always dropped
+ was undefined, with the exception that View surfaces (SurfaceView or TextureView) always dropped
excessive frames. Since {@link android.os.Build.VERSION_CODES#Q} the default behavior is to drop
excessive frames. Applications can opt out of this behavior for non-View surfaces (such as
ImageReader or SurfaceTexture) by targeting SDK {@link android.os.Build.VERSION_CODES#Q} and
@@ -3513,6 +3513,19 @@ final public class MediaCodec {
public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
/**
+ * Enable/disable low latency decoding mode.
+ * When enabled, the decoder doesn't hold input and output data more than
+ * required by the codec standards.
+ * The value is an Integer object containing the value 1 to enable
+ * or the value 0 to disable.
+ *
+ * @see #setParameters(Bundle)
+ * @see MediaFormat#KEY_LOW_LATENCY
+ */
+ public static final String PARAMETER_KEY_LOW_LATENCY =
+ MediaFormat.KEY_LOW_LATENCY;
+
+ /**
* Communicate additional parameter changes to the component instance.
* <b>Note:</b> Some of these parameter changes may silently fail to apply.
*
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f304f7cc5981..26e79364bb02 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -559,6 +559,14 @@ public final class MediaCodecInfo {
public static final String FEATURE_IntraRefresh = "intra-refresh";
/**
+ * <b>decoder only</b>: codec supports low latency decoding.
+ * If supported, clients can enable the low latency mode for the decoder.
+ * When the mode is enabled, the decoder doesn't hold input and output data more than
+ * required by the codec standards.
+ */
+ public static final String FEATURE_LowLatency = "low-latency";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -587,6 +595,7 @@ public final class MediaCodecInfo {
new Feature(FEATURE_FrameParsing, (1 << 4), false),
new Feature(FEATURE_MultipleFrames, (1 << 5), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
+ new Feature(FEATURE_LowLatency, (1 << 7), true),
};
private static final Feature[] encoderFeatures = {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 8b667f772867..8080f45642dc 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -211,6 +211,15 @@ public final class MediaFormat {
public static final String KEY_MIME = "mime";
/**
+ * An optional key describing the low latency decoding mode. This is an optional parameter
+ * that applies only to decoders. If enabled, the decoder doesn't hold input and output
+ * data more than required by the codec standards.
+ * The associated value is an integer (0 or 1): 1 when low-latency decoding is enabled,
+ * 0 otherwise. The default value is 0.
+ */
+ public static final String KEY_LOW_LATENCY = "low-latency";
+
+ /**
* A key describing the language of the content, using either ISO 639-1
* or 639-2/T codes. The associated value is a string.
*/
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index f421029909bd..7ed431d4660d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -1028,8 +1028,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see MediaFormat#COLOR_STANDARD_BT601_PAL
* @see MediaFormat#COLOR_STANDARD_BT601_NTSC
* @see MediaFormat#COLOR_STANDARD_BT2020
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_STANDARD = 35;
@@ -1040,8 +1038,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
* @see MediaFormat#COLOR_TRANSFER_ST2084
* @see MediaFormat#COLOR_TRANSFER_HLG
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_TRANSFER = 36;
@@ -1050,8 +1046,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
*
* @see MediaFormat#COLOR_RANGE_LIMITED
* @see MediaFormat#COLOR_RANGE_FULL
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_RANGE = 37;
// Add more here...
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fb581b532dd2..a315c1eefb52 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -139,6 +139,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given audio file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The audio file.
* @param size The desired thumbnail size.
@@ -231,6 +237,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given image file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The audio file.
* @param size The desired thumbnail size.
@@ -334,6 +346,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given video file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The video file.
* @param size The desired thumbnail size.
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index c7654e81e0b1..be2d5425cac1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -23,8 +23,6 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.statusbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.volume.CarVolumeDialogComponent;
-import com.android.systemui.volume.VolumeDialogComponent;
import javax.inject.Singleton;
@@ -57,10 +55,6 @@ public class CarSystemUIFactory extends SystemUIFactory {
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
}
- public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
- return new CarVolumeDialogComponent(systemUi, context);
- }
-
@Singleton
@Component(modules = ContextHolder.class)
public interface CarDependencyComponent {
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 5ec1baee5b14..b1067f8c3df5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -36,6 +36,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -102,4 +104,8 @@ abstract class CarSystemUIModule {
@IntoMap
@ClassKey(StatusBar.class)
public abstract SystemUI providesStatusBar(CarStatusBar statusBar);
+
+ @Binds
+ abstract VolumeDialogComponent bindVolumeDialogComponent(
+ CarVolumeDialogComponent carVolumeDialogComponent);
}
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 681d8f543575..a585727a25e9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -245,6 +245,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Inject
public CarStatusBar(
+ Context context,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -300,6 +301,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild,
NotifLog notifLog) {
super(
+ context,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 743ab4737bfb..886162f249a1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -183,11 +183,9 @@ public class UserGridRecyclerView extends RecyclerView {
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_USER_STOPPED);
- filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
mUserUpdateReceiver,
- UserHandle.ALL,
+ UserHandle.ALL, // Necessary because CarSystemUi lives in User 0
filter,
/* broadcastPermission= */ null,
/* scheduler= */ null);
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
index 71cc19b63ac1..4d6af95b3f9c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -19,15 +19,21 @@ package com.android.systemui.volume;
import android.content.Context;
import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.VolumeDialog;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Allows for adding car specific dialog when the volume dialog is created.
*/
+@Singleton
public class CarVolumeDialogComponent extends VolumeDialogComponent {
- public CarVolumeDialogComponent(SystemUI sysui, Context context) {
- super(sysui, context);
+ @Inject
+ public CarVolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator) {
+ super(context, keyguardViewMediator);
}
protected VolumeDialog createDefault() {
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
new file mode 100644
index 000000000000..5ae4957b494d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+
+ <style name="Theme.AlertDialogActivity.NoAnimation">
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+
+ <style name="Theme.AlertDialogActivity"
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
+ <style name="Theme.AlertDialogActivity.NoActionBar"
+ parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ </style>
+
+</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a0972498ab2a..665bde341515 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -476,10 +476,10 @@ public class BugreportProgressService extends Service {
}
private static void addScreenshotToIntent(Intent intent, BugreportInfo info) {
- final String screenshotFileName = info.name + ".png";
- final File screenshotFile = new File(BUGREPORT_DIR, screenshotFileName);
- final String screenshotFilePath = screenshotFile.getAbsolutePath();
- if (screenshotFile.length() > 0) {
+ final File screenshotFile = info.screenshotFiles.isEmpty()
+ ? null : info.screenshotFiles.get(0);
+ if (screenshotFile != null && screenshotFile.length() > 0) {
+ final String screenshotFilePath = screenshotFile.getAbsolutePath();
intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath);
}
return;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 22b0ab7dde4e..3e74970ee725 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -35,3 +35,7 @@
*;
}
-keep class androidx.core.app.CoreComponentFactory
+
+-keep public class * extends com.android.systemui.SystemUI {
+ public <init>(android.content.Context);
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index ad182fe57f81..22d1675cf17e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -53,7 +53,6 @@ public class WindowManagerWrapper {
public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
- public static final int TRANSIT_TASK_IN_PLACE = WindowManager.TRANSIT_TASK_IN_PLACE;
public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
public static final int TRANSIT_DOCK_TASK_FROM_RECENTS =
WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index ef9538dbef38..46b4c6b7d44c 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -170,7 +170,7 @@ public class CarrierTextController {
mSeparator = separator;
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mSimSlotsNumber = ((TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE)).getMaxPhoneCount();
+ Context.TELEPHONY_SERVICE)).getSupportedModemCount();
mSimErrorState = new boolean[mSimSlotsNumber];
mMainHandler = Dependency.get(Dependency.MAIN_HANDLER);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9bcccbc903dc..ac5e255289cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -99,6 +99,7 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
+import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -1437,6 +1438,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
private void handleScreenTurnedOff() {
+ final String tag = "KeyguardUpdateMonitor#handleScreenTurnedOff";
+ DejankUtils.startDetectingBlockingIpcs(tag);
checkIsHandlerThread();
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
@@ -1446,6 +1449,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
cb.onScreenTurnedOff();
}
}
+ DejankUtils.stopDetectingBlockingIpcs(tag);
}
private void handleDreamingStateChanged(int dreamStart) {
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index 0d24321d8db7..cf199c507c1a 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -49,6 +49,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -204,8 +205,11 @@ public class DependencyProvider {
@Singleton
@Provides
public AutoHideController provideAutoHideController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new AutoHideController(context, mainHandler);
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ IWindowManager iWindowManager) {
+ return new AutoHideController(context, mainHandler, notificationRemoteInputManager,
+ iWindowManager);
}
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 50f1b44b05b1..ddbabee283c2 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -41,6 +41,10 @@ public class LatencyTester extends SystemUI {
private static final String ACTION_TURN_ON_SCREEN =
"com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
+ public LatencyTester(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
if (!Build.IS_DEBUGGABLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3e068b0a0964..3a7a7f72f299 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -132,12 +132,15 @@ public class ScreenDecorations extends SystemUI implements Tunable {
return result;
}
+ public ScreenDecorations(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mHandler = startHandlerThread();
mHandler.post(this::startOnScreenDecorationsThread);
setupStatusBarPaddingIfNeeded();
- putComponent(ScreenDecorations.class, this);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
index e761a2be0b0f..e61268e0e1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
@@ -19,6 +19,7 @@ package com.android.systemui;
import android.app.Service;
import com.android.systemui.doze.DozeService;
+import com.android.systemui.keyguard.KeyguardService;
import dagger.Binds;
import dagger.Module;
@@ -30,8 +31,15 @@ import dagger.multibindings.IntoMap;
*/
@Module
public abstract class ServiceBinder {
+ /** */
@Binds
@IntoMap
@ClassKey(DozeService.class)
public abstract Service bindDozeService(DozeService service);
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardService.class)
+ public abstract Service bindKeyguardService(KeyguardService service);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index c54f6306ddb1..a8591bbb4dec 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -59,12 +59,13 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman
/** Only show once automatically in the process life. */
private boolean mHasShownHint;
- public SizeCompatModeActivityController() {
- this(ActivityManagerWrapper.getInstance());
+ public SizeCompatModeActivityController(Context context) {
+ this(context, ActivityManagerWrapper.getInstance());
}
@VisibleForTesting
- SizeCompatModeActivityController(ActivityManagerWrapper am) {
+ SizeCompatModeActivityController(Context context, ActivityManagerWrapper am) {
+ super(context);
am.registerTaskStackListener(new TaskStackChangeListener() {
@Override
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index b3fc69e8a49d..92fbd259b471 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -39,6 +39,10 @@ public class SliceBroadcastRelayHandler extends SystemUI {
private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+ public SliceBroadcastRelayHandler(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
if (DEBUG) Log.d(TAG, "Start");
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6cbefb..75700379caca 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -26,9 +26,13 @@ import java.io.PrintWriter;
import java.util.Map;
public abstract class SystemUI implements SysUiServiceProvider {
- public Context mContext;
+ protected final Context mContext;
public Map<Class<?>, Object> mComponents;
+ public SystemUI(Context context) {
+ mContext = context;
+ }
+
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 56b5d080acc0..189e5113e767 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,6 +42,8 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.util.NotificationChannels;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
@@ -193,18 +195,18 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
- obj = (SystemUI) Class.forName(clsName).newInstance();
+ Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
+ obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- } catch (InstantiationException ex) {
+ } catch (ClassNotFoundException
+ | NoSuchMethodException
+ | IllegalAccessException
+ | InstantiationException
+ | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
- mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
index 785038f2b7f7..a5a55983fe51 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
@@ -17,10 +17,12 @@
package com.android.systemui;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.pip.PipUI;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.volume.VolumeUI;
import dagger.Binds;
import dagger.Module;
@@ -32,12 +34,25 @@ import dagger.multibindings.IntoMap;
*/
@Module(includes = {RecentsModule.class})
public abstract class SystemUIBinder {
+
+ /** Inject into GarbageMonitor.Service. */
+ @Binds
+ @IntoMap
+ @ClassKey(GarbageMonitor.Service.class)
+ public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service);
+
/** Inject into KeyguardViewMediator. */
@Binds
@IntoMap
@ClassKey(KeyguardViewMediator.class)
public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+ /** Inject into PipUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipUI.class)
+ public abstract SystemUI bindPipUI(PipUI sysui);
+
/** Inject into PowerUI. */
@Binds
@IntoMap
@@ -50,9 +65,10 @@ public abstract class SystemUIBinder {
@ClassKey(Recents.class)
public abstract SystemUI bindRecents(Recents sysui);
- /** Inject into GarbageMonitor.Service. */
+ /** Inject into VolumeUI. */
@Binds
@IntoMap
- @ClassKey(GarbageMonitor.Service.class)
- public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service);
+ @ClassKey(VolumeUI.class)
+ public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 530dcdc98fbd..da4f3046badc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -48,7 +48,6 @@ import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
@@ -169,10 +168,6 @@ public class SystemUIFactory {
return new KeyguardIndicationController(context, indicationArea, lockIcon);
}
- public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
- return new VolumeDialogComponent(systemUi, context);
- }
-
@Module
public static class ContextHolder {
private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 0be6b12fa365..13d847bf93c4 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -16,11 +16,17 @@
package com.android.systemui;
+import android.content.Context;
+
/**
* Placeholder for any vendor-specific services.
*/
public class VendorServices extends SystemUI {
+ public VendorServices(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
// no-op
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 4c2afb0a14ca..cdc2623d34fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -172,12 +172,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
}
- public AuthController() {
- this(new Injector());
+ public AuthController(Context context) {
+ this(context, new Injector());
}
@VisibleForTesting
- AuthController(Injector injector) {
+ AuthController(Context context, Injector injector) {
+ super(context);
mInjector = injector;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index cc548d0c7f8e..9568a18e5ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -18,7 +18,6 @@ package com.android.systemui.bubbles;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -965,16 +964,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
+ intent);
return false;
}
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
- + "for intent: " + intent);
- return false;
- }
- if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
- Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
- + intent);
- return false;
- }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 521ebde7d2f0..6f953d584589 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
@@ -128,8 +130,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
Log.d(TAG, "onActivityViewReady: calling startActivity, "
+ "bubble=" + getBubbleKey());
}
+ Intent fillInIntent = new Intent();
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
try {
- mActivityView.startActivity(mBubbleIntent, options);
+ mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
} catch (RuntimeException e) {
// If there's a runtime exception here then there's something
// wrong with the intent, we can't really recover / try to populate
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index e8ef454bd466..c11127d1dc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -35,6 +35,10 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
+ public GlobalActionsComponent(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 7d52a9a08c2a..42f455af7df4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -117,6 +117,10 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha
private int mState;
+ public KeyboardUI(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mContext = super.mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
index b3481c52d7f2..4a33590f64d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
@@ -19,9 +19,13 @@ package com.android.systemui.keyguard;
import android.os.Handler;
import android.os.Message;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Dispatches the lifecycles keyguard gets from WindowManager on the main thread.
*/
+@Singleton
public class KeyguardLifecyclesDispatcher {
static final int SCREEN_TURNING_ON = 0;
@@ -37,6 +41,7 @@ public class KeyguardLifecyclesDispatcher {
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ @Inject
public KeyguardLifecyclesDispatcher(ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle) {
mScreenLifecycle = screenLifecycle;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 81247cd2f727..9f4056fdf65b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -33,25 +33,30 @@ import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.SystemUIApplication;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
- private KeyguardViewMediator mKeyguardViewMediator;
- private KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+
+ @Inject
+ public KeyguardService(KeyguardViewMediator keyguardViewMediator,
+ KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher) {
+ super();
+ mKeyguardViewMediator = keyguardViewMediator;
+ mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+ }
@Override
public void onCreate() {
((SystemUIApplication) getApplication()).startServicesIfNeeded();
- mKeyguardViewMediator =
- ((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class);
- mKeyguardLifecyclesDispatcher = new KeyguardLifecyclesDispatcher(
- Dependency.get(ScreenLifecycle.class),
- Dependency.get(WakefulnessLifecycle.class));
-
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a8027c025cf2..e0270decff08 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -685,9 +685,8 @@ public class KeyguardViewMediator extends SystemUI {
Context context,
FalsingManager falsingManager,
LockPatternUtils lockPatternUtils) {
- super();
+ super(context);
- mContext = context;
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -795,7 +794,6 @@ public class KeyguardViewMediator extends SystemUI {
synchronized (this) {
setupLocked();
}
- putComponent(KeyguardViewMediator.class, this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index aebadf936e0c..4c96de232810 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -59,6 +59,10 @@ public class RingtonePlayer extends SystemUI {
private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();
+ public RingtonePlayer(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mAsyncPlayer.setUsesWakeLock(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 682c76c6136a..f1e801b3a474 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -19,6 +19,7 @@ package com.android.systemui.pip;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.UserHandle;
@@ -30,15 +31,24 @@ import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controls the picture-in-picture window.
*/
+@Singleton
public class PipUI extends SystemUI implements CommandQueue.Callbacks {
private BasePipManager mPipManager;
private boolean mSupportsPip;
+ @Inject
+ public PipUI(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
PackageManager pm = mContext.getPackageManager();
@@ -59,7 +69,6 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks {
mPipManager.initialize(mContext);
getComponent(CommandQueue.class).addCallback(this);
- putComponent(PipUI.class, this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index a258f356bf53..98f0b2a35f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -103,7 +103,8 @@ public class PowerUI extends SystemUI {
private final BroadcastDispatcher mBroadcastDispatcher;
@Inject
- public PowerUI(BroadcastDispatcher broadcastDispatcher) {
+ public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher) {
+ super(context);
mBroadcastDispatcher = broadcastDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 14addb99c0c5..37743ec55517 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -2,6 +2,7 @@ package com.android.systemui.qs;
import android.content.Context;
import android.content.res.Resources;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
private boolean mListening;
protected int mMaxAllowedRows = 3;
+ // Prototyping with less rows
+ private final boolean mLessRows;
+
public TileLayout(Context context) {
this(context, null);
}
@@ -38,7 +42,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public TileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
+ mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0;
updateResources();
+
}
@Override
@@ -89,6 +95,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
+ if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
if (mColumns != columns) {
mColumns = columns;
requestLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e0ae8ed66f41..3fc139882693 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,6 +101,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
private final Context mContext;
+ private final PipUI mPipUI;
private SysUiState mSysUiState;
private final Handler mHandler;
private final NavigationBarController mNavBarController;
@@ -361,8 +362,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
long token = Binder.clearCallingIdentity();
try {
- final PipUI component = SysUiServiceProvider.getComponent(mContext, PipUI.class);
- component.setShelfHeight(visible, shelfHeight);
+ mPipUI.setShelfHeight(visible, shelfHeight);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -479,8 +479,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public OverviewProxyService(Context context, DeviceProvisionedController provisionController,
NavigationBarController navBarController, NavigationModeController navModeController,
StatusBarWindowController statusBarWinController,
- SysUiState sysUiState) {
+ SysUiState sysUiState, PipUI pipUI) {
mContext = context;
+ mPipUI = pipUI;
mHandler = new Handler();
mNavBarController = navBarController;
mStatusBarWinController = statusBarWinController;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a1b4a93c454a..0a8264bcef87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.provider.Settings;
@@ -37,7 +38,8 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks {
private final RecentsImplementation mImpl;
@Inject
- public Recents(RecentsImplementation impl) {
+ public Recents(Context context, RecentsImplementation impl) {
+ super(context);
mImpl = impl;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 07675e248906..df9791d1bd37 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -19,6 +19,7 @@ package com.android.systemui.shortcut;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.Log;
@@ -52,6 +53,10 @@ public class ShortcutKeyDispatcher extends SystemUI
protected final long SC_DOCK_LEFT = META_MASK | KeyEvent.KEYCODE_LEFT_BRACKET;
protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
+ public ShortcutKeyDispatcher(Context context) {
+ super(context);
+ }
+
/**
* Registers a shortcut key to window manager.
* @param shortcutCode packed representation of shortcut key code and meta information
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index cd2074fd64b8..c8b2b6aee0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -19,6 +19,7 @@ package com.android.systemui.stackdivider;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.Log;
@@ -50,6 +51,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
+ public Divider(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mWindowManager = new DividerWindowManager(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 36e04fe42ced..d6a8f906197d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -1101,6 +1101,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
// Need this class since CommandQueue already extends IStatusBar.Stub, so CommandQueueStart
// is needed so it can extend SystemUI.
public static class CommandQueueStart extends SystemUI {
+ public CommandQueueStart(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
putComponent(CommandQueue.class, new CommandQueue(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 16cdfaa18a53..e09e9cd75de2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -152,7 +152,9 @@ public class NavigationBarController implements Callbacks {
// Dependency problem.
AutoHideController autoHideController = isOnDefaultDisplay
? Dependency.get(AutoHideController.class)
- : new AutoHideController(context, mHandler);
+ : new AutoHideController(context, mHandler,
+ Dependency.get(NotificationRemoteInputManager.class),
+ Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreSystemUiVisibilityState();
mNavigationBars.append(displayId, navBar);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index a5b7fa7d1db6..f284f737b26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -31,6 +31,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.SynchronousUserSwitchObserver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -60,7 +61,7 @@ import com.android.systemui.util.NotificationChannels;
import java.util.List;
-/** The clsss to show notification(s) of instant apps. This may show multiple notifications on
+/** The class to show notification(s) of instant apps. This may show multiple notifications on
* splitted screen.
*/
public class InstantAppNotifier extends SystemUI
@@ -74,7 +75,9 @@ public class InstantAppNotifier extends SystemUI
private boolean mDockedStackExists;
private KeyguardStateController mKeyguardStateController;
- public InstantAppNotifier() {}
+ public InstantAppNotifier(Context context) {
+ super(context);
+ }
@Override
public void start() {
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 7bbe8188b402..98178252124a 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
@@ -473,8 +473,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
private final Rect mTmpRect = new Rect();
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
@@ -497,8 +496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private NotificationPanelView mNotificationPanel;
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
- private final NotificationGutsManager
- mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+ private final NotificationGutsManager mNotificationGutsManager;
private final NotificationSectionsManager mSectionsManager;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
@@ -518,6 +516,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
HeadsUpManagerPhone headsUpManager,
KeyguardBypassController keyguardBypassController,
FalsingManager falsingManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationGutsManager notificationGutsManager,
NotificationSectionsFeatureManager sectionsFeatureManager) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -526,6 +526,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mRoundnessManager = notificationRoundnessManager;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
mHeadsUpManager.addListener(mRoundnessManager);
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 5912cd7b6433..175d072e4c8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -29,7 +29,6 @@ import android.view.MotionEvent;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -68,12 +67,14 @@ public class AutoHideController implements CommandQueue.Callbacks {
};
@Inject
- public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+ public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ IWindowManager iWindowManager) {
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(this);
mHandler = handler;
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- mWindowManagerService = Dependency.get(IWindowManager.class);
+ mRemoteInputManager = notificationRemoteInputManager;
+ mWindowManagerService = iWindowManager;
mDisplayId = context.getDisplayId();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index da62d9b3adba..ce96005aa306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -98,9 +98,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
View statusbarView,
SysuiStatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
NotificationWakeUpCoordinator wakeUpCoordinator) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
- keyguardBypassController, wakeUpCoordinator,
+ keyguardBypassController, wakeUpCoordinator, keyguardStateController,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
statusbarView.findViewById(R.id.notification_panel),
@@ -116,6 +117,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
StatusBarStateController stateController,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
+ KeyguardStateController keyguardStateController,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
NotificationPanelView panelView,
@@ -160,7 +162,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
mWakeUpCoordinator = wakeUpCoordinator;
wakeUpCoordinator.addListener(this);
mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
+ mKeyguardStateController = keyguardStateController;
}
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 a7e7f085ffd7..c6d051d74239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,6 +29,7 @@ import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import androidx.collection.ArraySet;
@@ -390,7 +391,12 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
private void updateRegionForNotch(Region region) {
- DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout();
+ WindowInsets windowInsets = mStatusBarWindowView.getRootWindowInsets();
+ if (windowInsets == null) {
+ Log.w(TAG, "StatusBarWindowView is not attached.");
+ return;
+ }
+ DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index cac33044ea13..d95d2b7db4c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -132,7 +132,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private ActivityStarter mActivityStarter;
private KeyguardStateController mKeyguardStateController;
- private LockPatternUtils mLockPatternUtils;
private FlashlightController mFlashlightController;
private PreviewInflater mPreviewInflater;
private AccessibilityController mAccessibilityController;
@@ -231,7 +230,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mLockPatternUtils = new LockPatternUtils(mContext);
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
new ActivityIntentHelper(mContext));
mPreviewContainer = findViewById(R.id.preview_container);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 00736b73f12e..177294b1f45e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -89,6 +89,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -461,8 +462,11 @@ public class NotificationPanelView extends PanelView implements
ShadeController shadeController,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController,
DozeLog dozeLog) {
- super(context, attrs, falsingManager, dozeLog);
+ super(context, attrs, falsingManager, dozeLog, keyguardStateController,
+ (SysuiStatusBarStateController) statusBarStateController);
setWillNotDraw(!DEBUG);
mInjectionInflationController = injectionInflationController;
mFalsingManager = falsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 432d63648cfe..e8e5e1f60c51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -44,7 +44,6 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -144,10 +143,8 @@ public abstract class PanelView extends FrameLayout {
private boolean mGestureWaitForTouchSlop;
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
- protected final KeyguardStateController mKeyguardStateController = Dependency.get(
- KeyguardStateController.class);
- protected final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
+ protected final KeyguardStateController mKeyguardStateController;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -206,8 +203,11 @@ public abstract class PanelView extends FrameLayout {
}
public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager,
- DozeLog dozeLog) {
+ DozeLog dozeLog, KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController) {
super(context, attrs);
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */,
0.6f /* speedUpFactor */);
mFlingAnimationUtilsClosing = new FlingAnimationUtils(context, 0.5f /* maxLengthSeconds */,
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 2b80d2282661..9093687c367a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -620,6 +620,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Inject
public StatusBar(
+ Context context,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -674,6 +675,7 @@ public class StatusBar extends SystemUI implements DemoMode,
StatusBarWindowController statusBarWindowController,
StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder,
NotifLog notifLog) {
+ super(context);
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -959,7 +961,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
mStatusBarStateController, mKeyguardBypassController,
- mWakeUpCoordinator);
+ mKeyguardStateController, mWakeUpCoordinator);
mHeadsUpAppearanceController.readFrom(oldController);
mStatusBarWindowViewController.setStatusBarView(mStatusBarView);
updateAreThereNotifications();
@@ -1171,7 +1173,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, rowBinder);
+ mNotificationAlertingManager, rowBinder, mKeyguardStateController);
mNotificationListController =
new NotificationListController(
@@ -3600,7 +3602,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private void updateKeyguardState() {
mKeyguardStateController.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mKeyguardStateController.isMethodSecure(),
mStatusBarKeyguardViewManager.isOccluded());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 75b0cdcf13bd..8683586326d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -33,6 +33,8 @@ import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -61,8 +63,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
-import androidx.annotation.VisibleForTesting;
-
/**
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
* via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -301,8 +301,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowController.setKeyguardShowing(true);
- mKeyguardStateController.notifyKeyguardState(
- mShowing, mKeyguardStateController.isMethodSecure(),
+ mKeyguardStateController.notifyKeyguardState(mShowing,
mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
@@ -545,7 +544,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
mKeyguardStateController.notifyKeyguardState(mShowing,
- mKeyguardStateController.isMethodSecure(), mKeyguardStateController.isOccluded());
+ mKeyguardStateController.isOccluded());
launchPendingWakeupAction();
if (Dependency.get(KeyguardUpdateMonitor.class).needsSlowUnlockTransition()) {
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 3e0c268a5e70..f4a26badf719 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -89,8 +89,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
- private final KeyguardStateController mKeyguardStateController = Dependency.get(
- KeyguardStateController.class);
+ private final KeyguardStateController mKeyguardStateController;
private final NotificationViewHierarchyManager mViewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
private final NotificationLockscreenUserManager mLockscreenUserManager =
@@ -139,8 +138,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
NotificationAlertingManager notificationAlertingManager,
- NotificationRowBinderImpl notificationRowBinder) {
+ NotificationRowBinderImpl notificationRowBinder,
+ KeyguardStateController keyguardStateController) {
mContext = context;
+ mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 9a281cea314b..1def89b3af54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -35,7 +35,6 @@ import android.view.View;
import android.view.ViewParent;
import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -58,19 +57,16 @@ import javax.inject.Singleton;
public class StatusBarRemoteInputCallback implements Callback, Callbacks,
StatusBarStateController.StateListener {
- private final KeyguardStateController mKeyguardStateController = Dependency.get(
- KeyguardStateController.class);
- private final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ private final KeyguardStateController mKeyguardStateController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final ActivityStarter mActivityStarter;
+ private final ShadeController mShadeController;
private final Context mContext;
private final ActivityIntentHelper mActivityIntentHelper;
private final NotificationGroupManager mGroupManager;
private View mPendingWorkRemoteInputView;
private View mPendingRemoteInputView;
- private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private KeyguardManager mKeyguardManager;
private final CommandQueue mCommandQueue;
private int mDisabled2;
@@ -80,10 +76,19 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
/**
*/
@Inject
- public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) {
+ public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter, ShadeController shadeController) {
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
+ mShadeController = shadeController;
+ mActivityStarter = activityStarter;
mStatusBarStateController.addCallback(this);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index f21085ef0b1d..1ce776376678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -37,14 +37,17 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.InjectionInflationController;
@@ -88,6 +91,8 @@ public class StatusBarWindowViewController {
ShadeController shadeController,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
DozeLog dozeLog) {
mView = view;
mFalsingManager = falsingManager;
@@ -106,6 +111,8 @@ public class StatusBarWindowViewController {
shadeController,
notificationLockscreenUserManager,
notificationEntryManager,
+ keyguardStateController,
+ statusBarStateController,
dozeLog);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
@@ -472,6 +479,8 @@ public class StatusBarWindowViewController {
private final FalsingManager mFalsingManager;
private final PluginManager mPluginManager;
private final TunerService mTunerService;
+ private final KeyguardStateController mKeyguardStateController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
private ShadeController mShadeController;
private final NotificationLockscreenUserManager mNotificationLockScreenUserManager;
private final NotificationEntryManager mNotificationEntryManager;
@@ -490,6 +499,8 @@ public class StatusBarWindowViewController {
TunerService tunerService,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController,
DozeLog dozeLog) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
@@ -501,6 +512,8 @@ public class StatusBarWindowViewController {
mTunerService = tunerService;
mNotificationLockScreenUserManager = notificationLockscreenUserManager;
mNotificationEntryManager = notificationEntryManager;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mDozeLog = dozeLog;
}
@@ -537,6 +550,8 @@ public class StatusBarWindowViewController {
mShadeController,
mNotificationLockScreenUserManager,
mNotificationEntryManager,
+ mKeyguardStateController,
+ mStatusBarStateController,
mDozeLog);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index aefe201de45a..692c34c62dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -146,7 +146,7 @@ public interface KeyguardStateController extends CallbackController<Callback> {
/** **/
default void notifyKeyguardDoneFading() {}
/** **/
- default void notifyKeyguardState(boolean showing, boolean methodSecure, boolean occluded) {}
+ default void notifyKeyguardState(boolean showing, boolean occluded) {}
/**
* Callback for authentication events.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 392094d0288e..1cb2bd430199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -25,11 +25,12 @@ import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
import android.os.Trace;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import java.io.FileDescriptor;
@@ -42,25 +43,22 @@ import javax.inject.Singleton;
/**
*/
@Singleton
-public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
- implements KeyguardStateController, Dumpable {
+public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable {
private static final boolean DEBUG_AUTH_WITH_ADB = false;
private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private final Context mContext;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
- new LockedStateInvalidator();
+ new UpdateMonitorCallback();
private boolean mCanDismissLockScreen;
private boolean mShowing;
private boolean mSecure;
private boolean mOccluded;
- private boolean mListening;
private boolean mKeyguardFadingAway;
private long mKeyguardFadingAwayDelay;
private long mKeyguardFadingAwayDuration;
@@ -75,10 +73,10 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
/**
*/
@Inject
- public KeyguardStateControllerImpl(Context context) {
- mContext = context;
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mLockPatternUtils = new LockPatternUtils(context);
+ public KeyguardStateControllerImpl(Context context,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
update(true /* updateAlways */);
@@ -104,19 +102,12 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
if (!mCallbacks.contains(callback)) {
mCallbacks.add(callback);
}
- if (mCallbacks.size() != 0 && !mListening) {
- mListening = true;
- mKeyguardUpdateMonitor.registerCallback(this);
- }
}
@Override
public void removeCallback(@NonNull Callback callback) {
Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
- mListening = false;
- mKeyguardUpdateMonitor.removeCallback(this);
- }
+ mCallbacks.remove(callback);
}
@Override
@@ -140,19 +131,13 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
}
@Override
- public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
- if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
+ public void notifyKeyguardState(boolean showing, boolean occluded) {
+ if (mShowing == showing && mOccluded == occluded) return;
mShowing = showing;
- mSecure = secure;
mOccluded = occluded;
notifyKeyguardChanged();
}
- @Override
- public void onTrustChanged(int userId) {
- notifyKeyguardChanged();
- }
-
private void notifyKeyguardChanged() {
Trace.beginSection("KeyguardStateController#notifyKeyguardChanged");
// Copy the list to allow removal during callback.
@@ -191,7 +176,8 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
setKeyguardFadingAway(false);
}
- private void update(boolean updateAlways) {
+ @VisibleForTesting
+ void update(boolean updateAlways) {
Trace.beginSection("KeyguardStateController#update");
int user = KeyguardUpdateMonitor.getCurrentUser();
boolean secure = mLockPatternUtils.isSecure(user);
@@ -201,7 +187,7 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
- || trustManaged != mTrustManaged
+ || trustManaged != mTrustManaged || mTrusted != trusted
|| mFaceAuthEnabled != faceAuthEnabled;
if (changed || updateAlways) {
mSecure = secure;
@@ -284,7 +270,7 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled);
}
- private class LockedStateInvalidator extends KeyguardUpdateMonitorCallback {
+ private class UpdateMonitorCallback extends KeyguardUpdateMonitorCallback {
@Override
public void onUserSwitchComplete(int userId) {
update(false /* updateAlways */);
@@ -293,6 +279,7 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
@Override
public void onTrustChanged(int userId) {
update(false /* updateAlways */);
+ notifyKeyguardChanged();
}
@Override
@@ -327,11 +314,6 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
}
@Override
- public void onScreenTurnedOff() {
- update(false /* updateAlways */);
- }
-
- @Override
public void onKeyguardVisibilityChanged(boolean showing) {
update(false /* updateAlways */);
}
@@ -340,5 +322,5 @@ public class KeyguardStateControllerImpl extends KeyguardUpdateMonitorCallback
public void onBiometricsCleared() {
update(false /* alwaysUpdate */);
}
- };
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index b80b6d54427c..c2ed7df7cb2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -36,6 +36,10 @@ import com.android.systemui.statusbar.CommandQueue;
*/
public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
+ public TvStatusBar(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
putComponent(TvStatusBar.class, this);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 89aa7979e7d8..9a58a355c586 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -61,6 +61,10 @@ public class ThemeOverlayController extends SystemUI {
private ThemeOverlayManager mThemeManager;
private UserManager mUserManager;
+ public ThemeOverlayController(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
if (DEBUG) Log.d(TAG, "Start");
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index ff5bd03740bd..11885c55b51d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -60,6 +60,10 @@ public class StorageNotification extends SystemUI {
private NotificationManager mNotificationManager;
private StorageManager mStorageManager;
+ public StorageNotification(Context context) {
+ super(context);
+ }
+
private static class MoveInfo {
public int moveId;
public Bundle extras;
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index f35af90edc3c..8c60747dffc7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -38,6 +38,10 @@ public class NotificationChannels extends SystemUI {
public static String BATTERY = "BAT";
public static String HINTS = "HNT";
+ public NotificationChannels(Context context) {
+ super(context);
+ }
+
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
final NotificationChannel batteryChannel = new NotificationChannel(BATTERY,
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 2d5ebc4875cb..63db755ef09d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -526,11 +526,13 @@ public class GarbageMonitor implements Dumpable {
}
/** */
+ @Singleton
public static class Service extends SystemUI implements Dumpable {
private final GarbageMonitor mGarbageMonitor;
@Inject
- public Service(GarbageMonitor garbageMonitor) {
+ public Service(Context context, GarbageMonitor garbageMonitor) {
+ super(context);
mGarbageMonitor = garbageMonitor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index dcd0c58a5310..2224c9cce40a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -139,6 +139,12 @@ public class AsyncSensorManager extends SensorManager
@Override
protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ if (sensor == null) {
+ throw new IllegalArgumentException("sensor cannot be null");
+ }
mHandler.post(() -> {
if (!mInner.requestTriggerSensor(listener, sensor)) {
Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index d2f185a88bfd..25a5139bf661 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -27,7 +27,6 @@ import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.Dependency;
-import com.android.systemui.SystemUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -40,9 +39,13 @@ import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Implementation of VolumeComponent backed by the new volume dialog.
*/
+@Singleton
public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
VolumeDialogControllerImpl.UserActivityListener{
@@ -54,12 +57,12 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
- private final SystemUI mSysui;
protected final Context mContext;
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
+ private final KeyguardViewMediator mKeyguardViewMediator;
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
@@ -68,9 +71,10 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
400 // vibrateToSilentDebounce
);
- public VolumeDialogComponent(SystemUI sysui, Context context) {
- mSysui = sysui;
+ @Inject
+ public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator) {
mContext = context;
+ mKeyguardViewMediator = keyguardViewMediator;
mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
mController.setUserActivityListener(this);
// Allow plugins to reference the VolumeDialogController.
@@ -133,10 +137,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
@Override
public void onUserActivity() {
- final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
- if (kvm != null) {
- kvm.userActivity();
- }
+ mKeyguardViewMediator.userActivity();
}
private void applyConfiguration() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index f8cf79322b40..b74313975223 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -16,18 +16,22 @@
package com.android.systemui.volume;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.tiles.DndTile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class VolumeUI extends SystemUI {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
@@ -37,6 +41,12 @@ public class VolumeUI extends SystemUI {
private boolean mEnabled;
private VolumeDialogComponent mVolumeComponent;
+ @Inject
+ public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
+ super(context);
+ mVolumeComponent = volumeDialogComponent;
+ }
+
@Override
public void start() {
boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
@@ -45,8 +55,6 @@ public class VolumeUI extends SystemUI {
mEnabled = enableVolumeUi || enableSafetyWarning;
if (!mEnabled) return;
- mVolumeComponent = SystemUIFactory.getInstance()
- .createVolumeDialogComponent(this, mContext);
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
putComponent(VolumeComponent.class, getVolumeComponent());
setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 38537fdb00b1..1dd48634615b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -144,7 +144,7 @@ public class CarrierTextControllerTest extends SysuiTestCase {
mCarrierTextCallbackInfo = new CarrierTextController.CarrierTextCallbackInfo("",
new CharSequence[]{}, false, new int[]{});
- when(mTelephonyManager.getMaxPhoneCount()).thenReturn(3);
+ when(mTelephonyManager.getSupportedModemCount()).thenReturn(3);
mCarrierTextController = new CarrierTextController(mContext, SEPARATOR, true, true);
// This should not start listening on any of the real dependencies but will test that
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index a07f25a144b1..364ee666e17d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,7 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Assert;
@@ -50,9 +51,10 @@ public class ExpandHelperTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
Assert.sMainLooper = TestableLooper.get(this).getLooper();
Context context = getContext();
- mRow = new NotificationTestHelper(context).createRow();
+ mRow = new NotificationTestHelper(context, mDependency).createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 64ab060e14a2..c338d7031fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -102,7 +102,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
- mScreenDecorations = new ScreenDecorations() {
+ mScreenDecorations = new ScreenDecorations(mContext) {
@Override
public void start() {
super.start();
@@ -126,7 +126,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
}
};
- mScreenDecorations.mContext = mContext;
mScreenDecorations.mComponents = mContext.getComponents();
reset(mTunerService);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
index 3ea7150afca0..06999bc45e17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
@@ -58,13 +58,12 @@ public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
doReturn(true).when(mMockButton).show();
- mController = new SizeCompatModeActivityController(mMockAm) {
+ mController = new SizeCompatModeActivityController(mContext, mMockAm) {
@Override
RestartActivityButton createRestartButton(Context context) {
return mMockButton;
};
};
- mController.mContext = mContext;
ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(TaskStackChangeListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index 19e1a5cef183..a766885297fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.settingslib.SliceBroadcastRelay;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -44,6 +45,14 @@ import org.mockito.ArgumentCaptor;
public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+ private SliceBroadcastRelayHandler mRelayHandler;
+ private Context mSpyContext;
+ @Before
+ public void setup() {
+ mSpyContext = spy(mContext);
+
+ mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext);
+ }
@Test
public void testRegister() {
@@ -52,8 +61,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -63,8 +70,8 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);
- relayHandler.handleIntent(intent);
- verify(relayHandler.mContext).registerReceiver(any(), eq(value));
+ mRelayHandler.handleIntent(intent);
+ verify(mSpyContext).registerReceiver(any(), eq(value));
}
@Test
@@ -74,8 +81,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -84,14 +89,14 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
IntentFilter value = new IntentFilter(TEST_ACTION);
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+ verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
- relayHandler.handleIntent(intent);
- verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue()));
+ mRelayHandler.handleIntent(intent);
+ verify(mSpyContext).unregisterReceiver(eq(relay.getValue()));
}
@Test
@@ -101,12 +106,10 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
// No crash
}
@@ -118,9 +121,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
-
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
@@ -128,10 +128,10 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
IntentFilter value = new IntentFilter(TEST_ACTION);
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
- relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION));
+ verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+ relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index dcdb5c3f8e9e..e1eb3b0c81b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.Authenticator;
@@ -98,8 +99,7 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
- mAuthController = new TestableAuthController(new MockInjector());
- mAuthController.mContext = context;
+ mAuthController = new TestableAuthController(context, new MockInjector());
mAuthController.mComponents = mContext.getComponents();
mAuthController.start();
@@ -404,8 +404,8 @@ public class AuthControllerTest extends SysuiTestCase {
private int mBuildCount = 0;
private Bundle mLastBiometricPromptBundle;
- public TestableAuthController(Injector injector) {
- super(injector);
+ TestableAuthController(Context context, Injector injector) {
+ super(context, injector);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5a2b5e38222a..dd5211d4a1bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -158,7 +158,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mStatusBarWindowController.add(mStatusBarView, 120 /* height */);
// Need notifications for bubbles
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 392a7cbc4d6c..96ee079f1fee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -99,7 +99,7 @@ public class BubbleDataTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
MockitoAnnotations.initMocks(this);
mEntryA1 = createBubbleEntry(1, "a1", "package.a");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4958c649d532..8f4de3f114a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -682,8 +682,7 @@ public class PowerUITest extends SysuiTestCase {
}
private void createPowerUi() {
- mPowerUI = new PowerUI(mBroadcastDispatcher);
- mPowerUI.mContext = mContext;
+ mPowerUI = new PowerUI(mContext, mBroadcastDispatcher);
mPowerUI.mComponents = mContext.getComponents();
mPowerUI.mThermalService = mThermalServiceMock;
}
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 a0a410dfa361..e67aa69d48ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -52,7 +52,7 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 0569c55981fb..85a0fbd74550 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.android.collect.Lists;
@@ -83,6 +84,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
+ mDependency.injectMockDependency(KeyguardStateController.class);
mContext.addMockSystemService(UserManager.class, mUserManager);
mCurrentUserId = ActivityManager.getCurrentUser();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index de77af8f4d14..cb4096c454da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -42,6 +42,8 @@ import android.widget.RemoteViews;
import androidx.test.InstrumentationRegistry;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -51,6 +53,7 @@ import com.android.systemui.statusbar.notification.row.NotificationContentInflat
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.tests.R;
/**
@@ -75,8 +78,11 @@ public class NotificationTestHelper {
private ExpandableNotificationRow mRow;
private HeadsUpManagerPhone mHeadsUpManager;
- public NotificationTestHelper(Context context) {
+ public NotificationTestHelper(Context context, TestableDependency dependency) {
mContext = context;
+ dependency.injectMockDependency(NotificationMediaManager.class);
+ dependency.injectMockDependency(BubbleController.class);
+ dependency.injectMockDependency(StatusBarWindowController.class);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
StatusBarStateController stateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(stateController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 9e7250412499..6efa57d21cce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -103,7 +103,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index db2c8780e783..4103edee6255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -47,7 +47,7 @@ public class AboveShelfObserverTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(getContext());
+ mNotificationTestHelper = new NotificationTestHelper(getContext(), mDependency);
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
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
index edd0a10672fb..45e1721782a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -94,10 +95,11 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManager.class,
new NotificationGroupManager(mock(StatusBarStateController.class)));
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
mNotificationFilter = new NotificationFilter();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 8207a041ef50..170c661eb325 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -85,6 +86,7 @@ public class NotificationListControllerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 59c76a8cbb77..1be6f6178d46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -74,6 +74,7 @@ import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
@@ -137,13 +138,14 @@ public class NotificationDataTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManager.class,
new NotificationGroupManager(mock(StatusBarStateController.class)));
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mNotificationData = new TestableNotificationData(
mock(NotificationSectionsFeatureManager.class));
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), "");
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
Dependency.get(InitController.class).executePostInitTasks();
}
@@ -159,10 +161,11 @@ public class NotificationDataTest extends SysuiTestCase {
@Test
public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
ExpandableNotificationRow diffPkg =
- new NotificationTestHelper(getContext()).createRow("pkg", 4000,
+ new NotificationTestHelper(getContext(), mDependency).createRow("pkg", 4000,
Process.myUserHandle());
mNotificationData.add(diffPkg.getEntry());
@@ -189,7 +192,8 @@ public class NotificationDataTest extends SysuiTestCase {
@Test
public void testAppOpsRemoval() throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
ArraySet<Integer> expectedOps = new ArraySet<>();
@@ -221,7 +225,8 @@ public class NotificationDataTest extends SysuiTestCase {
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
when(mEnvironment.isNotificationForCurrentProfiles(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index c56a168a29d9..5e6c96313d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -82,7 +82,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index cc89504cb54d..444a6e5b4b13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -88,7 +88,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectMockDependency(BubbleController.class);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
// By default, have the shade visible/expanded.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index ccadcc35f37a..71c2e11f2fc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -77,7 +77,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
.setContentTitle("Title")
.setContentText("Text")
.setStyle(new Notification.BigTextStyle().bigText("big text"));
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
mNotificationInflater = new NotificationContentInflater(mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index db6b6130ebf1..41fe1735b5b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -62,6 +62,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -118,9 +119,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mDeviceProvisionedController);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mContext.putComponent(StatusBar.class, mStatusBar);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 49a64101eb84..d280f185edd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -44,7 +44,7 @@ public class NotificationCustomViewWrapperTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mRow = new NotificationTestHelper(mContext).createRow();
+ mRow = new NotificationTestHelper(mContext, mDependency).createRow();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 4f844f067566..4f45f680f475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -94,7 +94,7 @@ public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
mNotif = builder.build();
assertTrue(mNotif.hasMediaSession());
- mRow = new NotificationTestHelper(mContext).createRow(mNotif);
+ mRow = new NotificationTestHelper(mContext, mDependency).createRow(mNotif);
RemoteViews views = new RemoteViews(mContext.getPackageName(),
com.android.internal.R.layout.notification_template_material_big_media);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 546315900275..14e2fded6cdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -50,7 +50,7 @@ public class NotificationViewWrapperTest extends SysuiTestCase {
public void setup() throws Exception {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
mView = mock(View.class);
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 22d2585130fd..ddd2884ec311 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -45,7 +45,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroup = mNotificationTestHelper.createGroup();
mChildrenContainer = mGroup.getChildrenContainer();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 3f467eae1d57..34a309f1d80c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -70,7 +70,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mBypassController,
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
-> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
@@ -150,7 +150,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(mFirst, mSecond),
createSection(null, null)
});
- ExpandableNotificationRow row = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.getRow()).thenReturn(row);
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 11ae0cc34787..4b82f59e2a35 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
@@ -56,6 +56,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
@@ -70,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -167,6 +169,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mHeadsUpManager,
mKeyguardBypassController,
new FalsingManagerFake(),
+ mock(NotificationLockscreenUserManager.class),
+ mock(NotificationGutsManager.class),
new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext));
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelf(notificationShelf);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
index f614354a7691..16f02d9dd661 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.IWindowManager;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -38,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import org.junit.After;
import org.junit.Before;
@@ -58,7 +60,8 @@ public class AutoHideControllerTest extends SysuiTestCase {
public void setUp() {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mAutoHideController =
- spy(new AutoHideController(mContext, Dependency.get(Dependency.MAIN_HANDLER)));
+ spy(new AutoHideController(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+ mock(NotificationRemoteInputManager.class), mock(IWindowManager.class)));
mAutoHideController.mDisplayId = DEFAULT_DISPLAY;
mAutoHideController.mSystemUiVisibility = View.VISIBLE;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index a38094da3e1c..0216d2effef3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
import org.junit.Before;
@@ -61,11 +62,12 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private StatusBarStateController mStatusbarStateController;
private KeyguardBypassController mBypassController;
private NotificationWakeUpCoordinator mWakeUpCoordinator;
+ private KeyguardStateController mKeyguardStateController;
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
@@ -75,12 +77,14 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mStatusbarStateController = mock(StatusBarStateController.class);
mBypassController = mock(KeyguardBypassController.class);
mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
+ mKeyguardStateController = mock(KeyguardStateController.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mStatusbarStateController,
mBypassController,
mWakeUpCoordinator,
+ mKeyguardStateController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
@@ -158,6 +162,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mStatusbarStateController,
mBypassController,
mWakeUpCoordinator,
+ mKeyguardStateController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index ef9665a80848..6fcf550c56bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -29,6 +29,7 @@ import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Rule;
@@ -85,6 +87,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
+ mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectMockDependency(StatusBarWindowController.class);
+ mDependency.injectMockDependency(ConfigurationController.class);
mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mStatusBarWindowView,
mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
super.setUp();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 688a6fbc6d78..2b091f297184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -1,14 +1,11 @@
package com.android.systemui.statusbar.phone
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
-
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.KeyguardIndicationController
-
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -17,7 +14,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardBottomAreaTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index cb87d7d842a4..67b8e07f2bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -100,6 +100,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
+ mDependency.injectMockDependency(KeyguardStateController.class);
when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
.thenReturn(KeyguardSecurityModel.SecurityMode.None);
DejankUtils.setImmediate(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 93fdce173f12..b1580eedc43c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,8 +28,10 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +52,8 @@ public class LightBarTransitionsControllerTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(KeyguardStateController.class);
+ mDependency.injectMockDependency(StatusBarStateController.class);
mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
index 81e8abf5a058..098a69f5a897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -35,7 +35,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
import org.junit.Before;
@@ -63,6 +66,9 @@ public class NavigationBarButtonTest extends SysuiTestCase {
(SysuiTestableContext) mContext.createDisplayContext(display);
context.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(KeyguardStateController.class);
mNavBar = new NavigationBarView(context, null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
index be69f5f8a844..6433376cd2a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import org.junit.Before;
@@ -60,6 +61,8 @@ public class NavigationBarContextTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(AssistManager.class);
+
mGroup = new ContextualButtonGroup(GROUP_ID);
mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID);
mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
index cec7feb23752..991e49588417 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
@@ -31,6 +31,8 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.After;
@@ -51,6 +53,9 @@ public class NavigationBarInflaterViewTest extends SysuiTestCase {
@Before
public void setUp() {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(NavigationModeController.class);
mNavBarInflaterView = spy(new NavigationBarInflaterView(mContext, null));
doNothing().when(mNavBarInflaterView).createInflaters();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
index bb109bd931de..1e9378aea075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
@@ -28,7 +28,11 @@ import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +48,12 @@ public class NavigationBarTransitionsTest extends SysuiTestCase {
@Before
public void setup() {
mDependency.injectMockDependency(IWindowManager.class);
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(NavigationModeController.class);
+ mDependency.injectMockDependency(StatusBarStateController.class);
+ mDependency.injectMockDependency(KeyguardStateController.class);
+
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
NavigationBarView navBar = spy(new NavigationBarView(mContext, null));
when(navBar.getCurrentView()).thenReturn(navBar);
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 a0d264d81c7c..225423415421 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
@@ -33,6 +33,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -71,6 +72,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(BubbleController.class);
mHeadsUpManager = new HeadsUpManager(mContext) {};
when(mNotificationEntryManager.getPendingNotificationsIterator())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index dd274c7c09b9..493b74dd66cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -30,6 +30,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -57,6 +58,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(BubbleController.class);
initializeGroupManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 95e9e67cb830..2c1903733acc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
@@ -141,7 +142,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mStatusBarStateController,
new FalsingManagerFake());
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
- mKeyguardBypassController);
+ mKeyguardBypassController, mStatusBarStateController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
mNotificationPanelView.setBar(mPanelBar);
@@ -218,7 +219,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
private class TestableNotificationPanelView extends NotificationPanelView {
TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator,
PulseExpansionHandler expansionHandler,
- KeyguardBypassController bypassController) {
+ KeyguardBypassController bypassController,
+ SysuiStatusBarStateController statusBarStateController) {
super(
NotificationPanelViewTest.this.mContext,
null,
@@ -235,6 +237,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
new NotificationEntryManager(new NotificationData(mock(
NotificationSectionsFeatureManager.class), mock(NotifLog.class)),
mock(NotifLog.class)),
+ mock(KeyguardStateController.class),
+ statusBarStateController,
mock(DozeLog.class));
mNotificationStackScroller = mNotificationStackScrollLayout;
mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index aafcdd09fc10..3e07cff9b09b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -92,6 +93,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(StatusBarWindowController.class);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 266abcfc788f..d8a68b0c230c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -140,7 +140,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
// Create standard notification with contentIntent
mNotificationRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 24ec1097ea8b..210c9d6470fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -35,21 +35,37 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+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.RemoteInputController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper()
@@ -63,11 +79,33 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
@Before
public void setup() {
+ NotificationRemoteInputManager notificationRemoteInputManager =
+ mock(NotificationRemoteInputManager.class);
+ when(notificationRemoteInputManager.getController())
+ .thenReturn(mock(RemoteInputController.class));
mMetricsLogger = new FakeMetricsLogger();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mCommandQueue = new CommandQueue(mContext);
mContext.putComponent(CommandQueue.class, mCommandQueue);
+ mDependency.injectTestDependency(StatusBarStateController.class,
+ mock(SysuiStatusBarStateController.class));
mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ mDependency.injectTestDependency(NotificationRemoteInputManager.class,
+ notificationRemoteInputManager);
+ mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
+ mDependency.injectMockDependency(NotificationInterruptionStateProvider.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
+ mDependency.injectMockDependency(VisualStabilityManager.class);
+ mDependency.injectMockDependency(NotificationGutsManager.class);
+ mDependency.injectMockDependency(StatusBarWindowController.class);
+ mDependency.injectMockDependency(InitController.class);
+ NotificationData notificationData = mock(NotificationData.class);
+ when(notificationData.getNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
+ NotificationEntryManager entryManager =
+ mDependency.injectMockDependency(NotificationEntryManager.class);
+ when(entryManager.getNotificationData()).thenReturn(notificationData);
StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
@@ -77,7 +115,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
mock(NotificationAlertingManager.class),
- mock(NotificationRowBinderImpl.class));
+ mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 4b6ca56fc8e3..a65f5a503375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -24,18 +24,19 @@ import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.Intent;
-import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -47,14 +48,13 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
- @Mock private NotificationPresenter mPresenter;
- @Mock private UserManager mUserManager;
-
- // Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock private ActivityStarter mActivityStarter;
private int mCurrentUserId = 0;
private StatusBarRemoteInputCallback mRemoteInputCallback;
@@ -71,7 +71,9 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
- mock(NotificationGroupManager.class)));
+ mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
+ mKeyguardStateController, mStatusBarStateController, mActivityStarter,
+ mShadeController));
mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
}
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 12e9be15354f..f76dc613d462 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
@@ -286,6 +286,7 @@ public class StatusBarTest extends SysuiTestCase {
.thenReturn(mStatusBarWindowViewController);
mStatusBar = new StatusBar(
+ mContext,
mLightBarController,
mAutoHideController,
mKeyguardUpdateMonitor,
@@ -345,7 +346,6 @@ public class StatusBarTest extends SysuiTestCase {
mNotifLog);
// TODO: we should be able to call mStatusBar.start() and have all the below values
// initialized automatically.
- mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
mStatusBar.mStatusBarKeyguardViewManager = mStatusBarKeyguardViewManager;
mStatusBar.mStatusBarWindow = mStatusBarWindowView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 9f4dfb4453fc..0ef1acc11c84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -34,9 +34,11 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.InjectionInflationController;
@@ -61,6 +63,8 @@ public class StatusBarWindowViewTest extends SysuiTestCase {
@Mock private PluginManager mPluginManager;
@Mock private TunerService mTunerService;
@Mock private DragDownHelper mDragDownHelper;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockScreenUserManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
@@ -88,6 +92,8 @@ public class StatusBarWindowViewTest extends SysuiTestCase {
mTunerService,
mNotificationLockScreenUserManager,
mNotificationEntryManager,
+ mKeyguardStateController,
+ mStatusBarStateController,
mDozeLog)
.setShadeController(mShadeController)
.setStatusBarWindowView(mView)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
index d16dc168d74c..943674a7bf64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
@@ -40,6 +40,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.recents.OverviewProxyService;
import org.junit.Before;
import org.junit.Test;
@@ -68,6 +69,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mBubbleController = mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
TestableLooper.get(this).runWithLooper(() -> {
mKeyButtonView = new KeyButtonView(mContext, null, 0, mInputManager);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
new file mode 100644
index 000000000000..e57bbc1623f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardStateControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ private KeyguardStateController mKeyguardStateController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mKeyguardStateController = new KeyguardStateControllerImpl(mContext,
+ mKeyguardUpdateMonitor, mLockPatternUtils);
+ }
+
+ @Test
+ public void testAddCallback_registersListener() {
+ verify(mKeyguardUpdateMonitor).registerCallback(any());
+ }
+
+ @Test
+ public void testIsShowing() {
+ assertThat(mKeyguardStateController.isShowing()).isFalse();
+ mKeyguardStateController.notifyKeyguardState(true /* showing */, false /* occluded */);
+ assertThat(mKeyguardStateController.isShowing()).isTrue();
+ }
+
+ @Test
+ public void testIsMethodSecure() {
+ assertThat(mKeyguardStateController.isMethodSecure()).isFalse();
+
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isMethodSecure()).isTrue();
+ }
+
+ @Test
+ public void testIsOccluded() {
+ assertThat(mKeyguardStateController.isOccluded()).isFalse();
+ mKeyguardStateController.notifyKeyguardState(false /* showing */, true /* occluded */);
+ assertThat(mKeyguardStateController.isOccluded()).isTrue();
+ }
+
+ @Test
+ public void testCanSkipLockScreen() {
+ // Can skip because LockPatternUtils#isSecure is false
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue();
+
+ // Cannot skip after there's a password/pin/pattern
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isFalse();
+
+ // Unless user is authenticated
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue();
+ }
+
+ @Test
+ public void testIsUnlocked() {
+ // Is unlocked whenever the keyguard is not showing
+ assertThat(mKeyguardStateController.isShowing()).isFalse();
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+
+ // Unlocked if showing, but insecure
+ mKeyguardStateController.notifyKeyguardState(true /* showing */, false /* occluded */);
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+
+ // Locked if showing, and requires password
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isUnlocked()).isFalse();
+
+ // But unlocked after #getUserCanSkipBouncer allows it
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+ }
+
+ @Test
+ public void testIsTrusted() {
+ assertThat(mKeyguardStateController.isTrusted()).isFalse();
+
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+
+ assertThat(mKeyguardStateController.isTrusted()).isTrue();
+ }
+
+ @Test
+ public void testCallbacksAreInvoked() {
+ KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
+ mKeyguardStateController.addCallback(callback);
+
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+
+ verify(callback).onUnlockedChanged();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index bc468bf2fb82..390e812b3613 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -106,7 +106,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testSendRemoteInput_intentContainsResultsAndSource() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
+ .createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
setTestPendingIntent(view);
@@ -127,7 +128,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
private UserHandle getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)
throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
DUMMY_MESSAGE_APP_PKG,
UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID),
toUser);
@@ -169,7 +170,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testNoCrashWithoutVisibilityListener() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
+ .createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
view.setOnVisibilityChangedListener(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 0d56cbe84eb0..b5e4cb965de8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -115,6 +116,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
});
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 514eb77c3201..950fa8d4038a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -91,7 +91,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected final Context mContext;
protected final SystemSupport mSystemSupport;
protected final WindowManagerInternal mWindowManagerService;
- private final GlobalActionPerformer mGlobalActionPerformer;
+ private final SystemActionPerformer mSystemActionPerformer;
private final AccessibilityWindowManager mA11yWindowManager;
private final DisplayManager mDisplayManager;
private final PowerManager mPowerManager;
@@ -213,7 +213,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
mContext = context;
mWindowManagerService = windowManagerInternal;
@@ -222,7 +222,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mAccessibilityServiceInfo = accessibilityServiceInfo;
mLock = lock;
mSecurityPolicy = securityPolicy;
- mGlobalActionPerformer = globalActionPerfomer;
+ mSystemActionPerformer = systemActionPerfomer;
mSystemSupport = systemSupport;
mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
mA11yWindowManager = a11yWindowManager;
@@ -760,7 +760,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
}
- return mGlobalActionPerformer.performGlobalAction(action);
+ return mSystemActionPerformer.performSystemAction(action);
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3b105ad30bd0..91269c7ab37e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -198,7 +198,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final MainHandler mMainHandler;
- private final GlobalActionPerformer mGlobalActionPerformer;
+ private final SystemActionPerformer mSystemActionPerformer;
private MagnificationController mMagnificationController;
@@ -272,7 +272,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
mMainHandler = new MainHandler(mContext.getMainLooper());
- mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
+ mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -730,7 +730,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
- mSecurityPolicy, this, mWindowManagerService, mGlobalActionPerformer,
+ mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager, flags);
onUserStateChangedLocked(getCurrentUserStateLocked());
}
@@ -1450,7 +1450,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- this, mWindowManagerService, mGlobalActionPerformer,
+ this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager);
} else if (userState.mBoundServices.contains(service)) {
continue;
@@ -2482,7 +2482,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
AccessibilityManagerService.this, mWindowManagerService,
- mGlobalActionPerformer, mA11yWindowManager) {
+ mSystemActionPerformer, mA11yWindowManager) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index d7f61e5371d5..e7f3ccc9f883 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -71,9 +71,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+ SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer, awm);
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
mUserStateWeakReference = new WeakReference<UserState>(userState);
mIntent = new Intent().setComponent(mComponentName);
mMainHandler = mainHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index b9b2654b93cc..19ac0d3c1024 100644
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -1,17 +1,17 @@
/*
- ** 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.
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.accessibility;
@@ -40,12 +40,12 @@ import java.util.function.Supplier;
/**
* Handle the back-end of AccessibilityService#performGlobalAction
*/
-public class GlobalActionPerformer {
+public class SystemActionPerformer {
private final WindowManagerInternal mWindowManagerService;
private final Context mContext;
private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
- public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
+ public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
mContext = context;
mWindowManagerService = windowManagerInternal;
mScreenshotHelperSupplier = null;
@@ -53,13 +53,16 @@ public class GlobalActionPerformer {
// Used to mock ScreenshotHelper
@VisibleForTesting
- public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+ public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
Supplier<ScreenshotHelper> screenshotHelperSupplier) {
this(context, windowManagerInternal);
mScreenshotHelperSupplier = screenshotHelperSupplier;
}
- public boolean performGlobalAction(int action) {
+ /**
+ * Performe the system action matching the given action id.
+ */
+ public boolean performSystemAction(int action) {
final long identity = Binder.clearCallingIdentity();
try {
switch (action) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 79d975dac2b2..7dd4a7089954 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -87,7 +87,7 @@ class UiAutomationManager {
AccessibilitySecurityPolicy securityPolicy,
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager awm, int flags) {
synchronized (mLock) {
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
@@ -108,7 +108,7 @@ class UiAutomationManager {
mSystemSupport = systemSupport;
mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
- globalActionPerfomer, awm);
+ systemActionPerfomer, awm);
mUiAutomationServiceOwner = owner;
mUiAutomationFlags = flags;
mUiAutomationServiceInfo = accessibilityServiceInfo;
@@ -226,9 +226,9 @@ class UiAutomationManager {
int id, Handler mainHandler, Object lock,
AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+ SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
awm);
mMainHandler = mainHandler;
}
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index 110847dd54c8..c6853a5119ec 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,12 +16,12 @@
package com.android.server;
-import com.android.internal.util.ConcurrentUtils;
-import com.android.server.location.ContextHubService;
-import com.android.server.SystemServerInitThreadPool;
import android.content.Context;
import android.util.Log;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.location.ContextHubService;
+
import java.util.concurrent.Future;
class ContextHubSystemService extends SystemService {
@@ -32,7 +32,7 @@ class ContextHubSystemService extends SystemService {
public ContextHubSystemService(Context context) {
super(context);
- mInit = SystemServerInitThreadPool.get().submit(() -> {
+ mInit = SystemServerInitThreadPool.submit(() -> {
mContextHubService = new ContextHubService(context);
}, "Init ContextHubSystemService");
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index bd5ad960a886..73c852083cfd 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -162,7 +162,7 @@ public class PersistentDataBlockService extends SystemService {
@Override
public void onStart() {
// Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
enforceChecksumValidity();
formatIfOemUnlockEnabled();
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 19f4089b07af..5ed94e380b5b 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -67,23 +67,26 @@ public class SystemServerInitThreadPool {
}
/**
- * Gets the singleton.
+ * Submits a task for execution.
*
* @throws IllegalStateException if it hasn't been started or has been shut down already.
*/
- public static SystemServerInitThreadPool get() {
+ public static @NonNull Future<?> submit(@NonNull Runnable runnable,
+ @NonNull String description) {
+ Preconditions.checkNotNull(description, "description cannot be null");
+
+ SystemServerInitThreadPool instance;
synchronized (LOCK) {
Preconditions.checkState(sInstance != null, "Cannot get " + TAG
+ " - it has been shut down");
- return sInstance;
+ instance = sInstance;
}
+
+ return instance.submitTask(runnable, description);
}
- /**
- * Submits a task for execution.
- */
- public @NonNull Future<?> submit(@NonNull Runnable runnable, @NonNull String description) {
- Preconditions.checkNotNull(description, "description cannot be null");
+ private @NonNull Future<?> submitTask(@NonNull Runnable runnable,
+ @NonNull String description) {
synchronized (mPendingTasks) {
Preconditions.checkState(!mShutDown, TAG + " already shut down");
mPendingTasks.add(description);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0f8a3b54acc5..447ed59a775e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -385,7 +385,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
- int numPhones = TelephonyManager.getDefault().getMaxPhoneCount();
+ int numPhones = TelephonyManager.getDefault().getSupportedModemCount();
if (DBG) log("TelephonyRegistry: ctor numPhones=" + numPhones);
mNumPhones = numPhones;
mCallState = new int[numPhones];
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b9d7c687704c..a51746767f0f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -288,7 +288,7 @@ final class UiModeManagerService extends SystemService {
updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
// Update the initial, static configurations.
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
synchronized (mLock) {
updateConfigurationLocked();
sendConfigurationLocked();
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 6ffd8a9180ba..5c840ada86d4 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -1101,7 +1101,7 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
boolean sepNeeded = false;
- if (dumpAll || isCheckin) {
+ if ((dumpAll || isCheckin) && !currentOnly) {
mWriteLock.lock();
try {
ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 9eb0d50dd789..1b132120f709 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -1089,7 +1089,7 @@ public class FaceService extends BiometricServiceBase {
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
// Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't
// blocked
- SystemServerInitThreadPool.get().submit(() -> mHandler.post(this::getFaceDaemon),
+ SystemServerInitThreadPool.submit(() -> mHandler.post(this::getFaceDaemon),
TAG + ".onStart");
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 320e1022873c..d85af2eb0b32 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -725,7 +725,7 @@ public class FingerprintService extends BiometricServiceBase {
public void onStart() {
super.onStart();
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+ SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart");
}
@Override
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 027e2fb5ccaa..0fabd9aef373 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -25,6 +25,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
@@ -186,6 +187,43 @@ public final class CompatConfig {
}
return overrideExists;
}
+ /**
+ * Overrides the enabled state for a given change and app. This method is intended to be used
+ * *only* for debugging purposes.
+ *
+ * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
+ *
+ * @param overrides list of overrides to default changes config.
+ * @param packageName app for which the overrides will be applied.
+ */
+ public void addOverrides(
+ CompatibilityChangeConfig overrides, String packageName) {
+ synchronized (mChanges) {
+ for (Long changeId: overrides.enabledChanges()) {
+ addOverride(changeId, packageName, true);
+ }
+ for (Long changeId: overrides.disabledChanges()) {
+ addOverride(changeId, packageName, false);
+ }
+ }
+ }
+
+ /**
+ * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
+ * {@link #addAppOverrides(CompatibilityChangeConfig, String)} for a certain package.
+ *
+ * <p>This restores the default behaviour for the given change and app, once any app
+ * processes have been restarted.
+ *
+ * @param packageName The package for which the overrides should be purged.
+ */
+ public void removePackageOverrides(String packageName) {
+ synchronized (mChanges) {
+ for (int i = 0; i < mChanges.size(); ++i) {
+ mChanges.valueAt(i).removePackageOverride(packageName);
+ }
+ }
+ }
/**
* Dumps the current list of compatibility config information.
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
new file mode 100644
index 000000000000..2b7cdb0cbce9
--- /dev/null
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index a7378880a91d..8a7dcc12baae 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -23,6 +23,7 @@ import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.compat.ChangeReporter;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -100,6 +101,17 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
+ public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
+ CompatConfig.get().addOverrides(overrides, packageName);
+ }
+
+ @Override
+ public void clearOverrides(String packageName) {
+ CompatConfig config = CompatConfig.get();
+ config.removePackageOverrides(packageName);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7e6e6680905e..f1af73a12276 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -71,7 +71,6 @@ import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IInputMonitorHost;
-import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -186,9 +185,6 @@ public class InputManagerService extends IInputManager.Stub
IInputFilter mInputFilter; // guarded by mInputFilterLock
InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
- private IWindow mFocusedWindow;
- private boolean mFocusedWindowHasCapture;
-
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);
@@ -544,20 +540,16 @@ public class InputManagerService extends IInputManager.Stub
}
/**
- * Registers an input channel so that it can be used as an input event target.
+ * Registers an input channel so that it can be used as an input event target. The channel is
+ * registered with a generated token.
+ *
* @param inputChannel The input channel to register.
- * @param inputWindowHandle The handle of the input window associated with the
- * input channel, or null if none.
*/
- public void registerInputChannel(InputChannel inputChannel, IBinder token) {
+ public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
-
- if (token == null) {
- token = new Binder();
- }
- inputChannel.setToken(token);
+ inputChannel.setToken(new Binder());
nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
}
@@ -1513,26 +1505,9 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void requestPointerCapture(IBinder windowToken, boolean enabled) {
- if (mFocusedWindow == null || mFocusedWindow.asBinder() != windowToken) {
- Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
- + windowToken);
- return;
- }
- if (mFocusedWindowHasCapture == enabled) {
- Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
- return;
- }
- setPointerCapture(enabled);
- }
-
- private void setPointerCapture(boolean enabled) {
- if (mFocusedWindowHasCapture != enabled) {
- mFocusedWindowHasCapture = enabled;
- try {
- mFocusedWindow.dispatchPointerCaptureChanged(enabled);
- } catch (RemoteException ex) {
- /* ignore */
- }
+ boolean requestConfigurationRefresh =
+ mWindowManagerCallbacks.requestPointerCapture(windowToken, enabled);
+ if (requestConfigurationRefresh) {
nativeSetPointerCapture(mPtr, enabled);
}
}
@@ -1829,16 +1804,11 @@ public class InputManagerService extends IInputManager.Stub
// Native callback
private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
- if (mFocusedWindow != null) {
- if (mFocusedWindow.asBinder() == newToken) {
- Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
- + mFocusedWindow);
- return;
- }
- setPointerCapture(false);
+ final boolean requestConfigurationRefresh =
+ mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken);
+ if (requestConfigurationRefresh) {
+ nativeSetPointerCapture(mPtr, false);
}
-
- mFocusedWindow = IWindow.Stub.asInterface(newToken);
}
// Native callback.
@@ -2116,6 +2086,20 @@ public class InputManagerService extends IInputManager.Stub
* @param touchedToken The token for the window that received the input event.
*/
void onPointerDownOutsideFocus(IBinder touchedToken);
+
+ /**
+ * Called when the focused window has changed.
+ *
+ * @return true if we want to request a configuration refresh.
+ */
+ boolean notifyFocusChanged(IBinder oldToken, IBinder newToken);
+
+ /**
+ * Called by the client to request pointer capture.
+ *
+ * @return true if we want to request a configuration refresh.
+ */
+ boolean requestPointerCapture(IBinder windowToken, boolean enabled);
}
/**
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 93b2c55d4735..e90612e54105 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -66,7 +66,7 @@ public final class RuleEvaluationEngine {
case DENY:
return IntegrityCheckResult.deny(matchedRule);
default:
- Slog.i(TAG, "Matched a non-DENY rule: " + matchedRule);
+ Slog.e(TAG, "Matched a non-DENY rule: " + matchedRule);
return IntegrityCheckResult.allow();
}
}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index bd2c1c65e637..641650557f4c 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -16,7 +16,12 @@
package com.android.server.integrity.engine;
+import android.util.Slog;
+
import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.Formula;
+import com.android.server.integrity.model.OpenFormula;
import com.android.server.integrity.model.Rule;
import java.util.List;
@@ -27,9 +32,14 @@ import java.util.List;
*/
final class RuleEvaluator {
+ private static final String TAG = "RuleEvaluator";
+
/**
* Match the list of rules against an app install metadata.
*
+ * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
+ * only. All rules are OR'ed together by default.
+ *
* @param rules The list of rules to evaluate.
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
* against.
@@ -38,15 +48,79 @@ final class RuleEvaluator {
*/
static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
for (Rule rule : rules) {
- if (isMatch(rule, appInstallMetadata)) {
+ if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
return rule;
}
}
return Rule.EMPTY;
}
+ /**
+ * Match a rule against app install metadata.
+ */
private static boolean isMatch(Rule rule, AppInstallMetadata appInstallMetadata) {
- // TODO: Add matching logic
+ return isMatch(rule.getFormula(), appInstallMetadata);
+ }
+
+ private static boolean isMatch(Formula formula, AppInstallMetadata appInstallMetadata) {
+ if (formula instanceof AtomicFormula) {
+ AtomicFormula atomicFormula = (AtomicFormula) formula;
+ switch (atomicFormula.getKey()) {
+ case PACKAGE_NAME:
+ return atomicFormula.isMatch(appInstallMetadata.getPackageName());
+ case APP_CERTIFICATE:
+ return atomicFormula.isMatch(appInstallMetadata.getAppCertificate());
+ case INSTALLER_NAME:
+ return atomicFormula.isMatch(appInstallMetadata.getInstallerName());
+ case INSTALLER_CERTIFICATE:
+ return atomicFormula.isMatch(appInstallMetadata.getInstallerCertificate());
+ case VERSION_CODE:
+ return atomicFormula.isMatch(appInstallMetadata.getVersionCode());
+ case PRE_INSTALLED:
+ return atomicFormula.isMatch(appInstallMetadata.isPreInstalled());
+ default:
+ Slog.i(TAG, String.format("Returned no match for unknown key %s",
+ atomicFormula.getKey()));
+ return false;
+ }
+ } else if (formula instanceof OpenFormula) {
+ OpenFormula openFormula = (OpenFormula) formula;
+ // A rule is in disjunctive normal form, so there are no OR connectors.
+ switch (openFormula.getConnector()) {
+ case NOT:
+ // NOT connector has only 1 formula attached.
+ return !isMatch(openFormula.getFormulas().get(0), appInstallMetadata);
+ case AND:
+ return openFormula.getFormulas().stream().allMatch(
+ subFormula -> isMatch(subFormula, appInstallMetadata));
+ default:
+ Slog.i(TAG, String.format("Returned no match for unknown connector %s",
+ openFormula.getConnector()));
+ return false;
+ }
+ }
+
return false;
}
+
+ private static boolean isConjunctionOfFormulas(Formula formula) {
+ if (formula == null) {
+ return false;
+ }
+ if (isAtomicFormula(formula)) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.AND
+ && openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
+ }
+
+ private static boolean isAtomicFormula(Formula formula) {
+ if (formula instanceof AtomicFormula) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.NOT
+ && openFormula.getFormulas().get(0) instanceof AtomicFormula;
+ }
}
diff --git a/services/core/java/com/android/server/integrity/model/AtomicFormula.java b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
index 3859d03d01d2..b9b46e3e1aae 100644
--- a/services/core/java/com/android/server/integrity/model/AtomicFormula.java
+++ b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
@@ -20,6 +20,9 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
+import android.util.Slog;
+
+import java.util.Objects;
/**
* Represents a simple formula consisting of an app install metadata field and a value.
@@ -28,7 +31,9 @@ import android.annotation.Nullable;
*/
public final class AtomicFormula extends Formula {
- enum Key {
+ private static final String TAG = "AtomicFormula";
+
+ public enum Key {
PACKAGE_NAME,
APP_CERTIFICATE,
INSTALLER_NAME,
@@ -37,7 +42,7 @@ public final class AtomicFormula extends Formula {
PRE_INSTALLED
}
- enum Operator {
+ public enum Operator {
EQ,
LT,
LE,
@@ -127,11 +132,85 @@ public final class AtomicFormula extends Formula {
return mBoolValue.toString();
}
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the string value.
+ *
+ * @param value String value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(String value) {
+ switch (mOperator) {
+ case EQ:
+ return mStringValue.equals(value);
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mStringValue));
+ return false;
+ }
+
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the integer value.
+ *
+ * @param value Integer value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(int value) {
+ switch (mOperator) {
+ case EQ:
+ return mIntValue == value;
+ case LE:
+ return mIntValue <= value;
+ case LT:
+ return mIntValue < value;
+ case GE:
+ return mIntValue >= value;
+ case GT:
+ return mIntValue > value;
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mIntValue));
+ return false;
+ }
+
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the boolean value.
+ *
+ * @param value Boolean value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(boolean value) {
+ switch (mOperator) {
+ case EQ:
+ return mBoolValue == value;
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mBoolValue));
+ return false;
+ }
+
@Override
public String toString() {
return String.format("%s %s %s", mKey, mOperator, getValue());
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AtomicFormula that = (AtomicFormula) o;
+ return mKey == that.mKey
+ && mOperator == that.mOperator
+ && Objects.equals(mStringValue, that.mStringValue)
+ && Objects.equals(mIntValue, that.mIntValue)
+ && Objects.equals(mBoolValue, that.mBoolValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mKey, mOperator, mStringValue, mIntValue, mBoolValue);
+ }
+
private void validateOperator(Key key, Operator operator) {
boolean validOperator;
switch (key) {
@@ -146,6 +225,7 @@ public final class AtomicFormula extends Formula {
validOperator = true;
break;
default:
+ Slog.i(TAG, String.format("Found operator %s for key %s", operator, key));
validOperator = false;
}
if (!validOperator) {
diff --git a/services/core/java/com/android/server/integrity/model/Formula.java b/services/core/java/com/android/server/integrity/model/Formula.java
index 4cfa2c76adf2..9db445378dce 100644
--- a/services/core/java/com/android/server/integrity/model/Formula.java
+++ b/services/core/java/com/android/server/integrity/model/Formula.java
@@ -19,6 +19,6 @@ package com.android.server.integrity.model;
/**
* Represents a rule logic/content.
*/
-abstract class Formula {
+public abstract class Formula {
}
diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java
index 8d0f31e1b0c2..21da629fba2e 100644
--- a/services/core/java/com/android/server/integrity/model/OpenFormula.java
+++ b/services/core/java/com/android/server/integrity/model/OpenFormula.java
@@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Represents a complex formula consisting of other simple and complex formulas.
@@ -29,7 +30,7 @@ import java.util.List;
*/
public final class OpenFormula extends Formula {
- enum Connector {
+ public enum Connector {
AND,
OR,
NOT
@@ -64,6 +65,24 @@ public final class OpenFormula extends Formula {
return sb.toString();
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ OpenFormula that = (OpenFormula) o;
+ return mConnector == that.mConnector
+ && mFormulas.equals(that.mFormulas);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConnector, mFormulas);
+ }
+
private void validateFormulas(Connector connector, List<Formula> formulas) {
switch (connector) {
case AND:
diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java
index 41611d0f7154..63b9b911ff4f 100644
--- a/services/core/java/com/android/server/integrity/model/Rule.java
+++ b/services/core/java/com/android/server/integrity/model/Rule.java
@@ -18,6 +18,8 @@ package com.android.server.integrity.model;
import static com.android.internal.util.Preconditions.checkNotNull;
+import java.util.Objects;
+
/**
* Represent rules to be used in the rule evaluation engine to match against app installs.
*
@@ -66,4 +68,22 @@ public final class Rule {
public String toString() {
return String.format("Rule: %s, %s", mFormula, mEffect);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Rule that = (Rule) o;
+ return Objects.equals(mFormula, that.mFormula)
+ && Objects.equals(mEffect, that.mEffect);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFormula, mEffect);
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index c6226fa98197..8bf01a366f65 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,6 +74,8 @@ import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.LocalServices;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -177,6 +179,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
+ private static final int UPDATE_LOW_POWER_MODE = 1;
private static final int SET_REQUEST = 3;
private static final int INJECT_NTP_TIME = 5;
// PSDS stands for Predicted Satellite Data Service
@@ -360,6 +363,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private boolean mDisableGpsForPowerManager = false;
/**
+ * True if the device idle controller has determined that the device is stationary. This is only
+ * updated when the device enters idle mode.
+ */
+ private volatile boolean mIsDeviceStationary = false;
+
+ /**
* Properties loaded from PROPERTIES_FILE.
* It must be accessed only inside {@link #mHandler}.
*/
@@ -451,6 +460,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
return mGnssNavigationMessageProvider;
}
+
+ private final DeviceIdleInternal.StationaryListener mDeviceIdleStationaryListener =
+ isStationary -> {
+ mIsDeviceStationary = isStationary;
+ // Call updateLowPowerMode on handler thread so it's always called from the same
+ // thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -467,11 +485,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case ALARM_TIMEOUT:
hibernate();
break;
- case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ DeviceIdleInternal deviceIdleService = LocalServices.getService(
+ DeviceIdleInternal.class);
+ if (mPowerManager.isDeviceIdleMode()) {
+ deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
+ } else {
+ deviceIdleService.unregisterStationaryListener(
+ mDeviceIdleStationaryListener);
+ }
+ // Intentional fall-through.
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case Intent.ACTION_SCREEN_OFF:
case Intent.ACTION_SCREEN_ON:
- updateLowPowerMode();
+ // Call updateLowPowerMode on handler thread so it's always called from the
+ // same thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
@@ -529,10 +558,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void updateLowPowerMode() {
- // Disable GPS if we are in device idle mode.
- boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+ // Disable GPS if we are in device idle mode and the device is stationary.
+ boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
+ final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
switch (result.locationMode) {
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
@@ -2005,6 +2033,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case REPORT_SV_STATUS:
handleReportSvStatus((SvStatusInfo) msg.obj);
break;
+ case UPDATE_LOW_POWER_MODE:
+ updateLowPowerMode();
+ break;
}
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4293ccadd1fb..4457e9c312d8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,7 +49,6 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -88,7 +87,6 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
@@ -5251,18 +5249,6 @@ public class NotificationManagerService extends SystemService {
+ intent);
return false;
}
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
- Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
- + "for intent: " + intent);
- return false;
- }
- if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
- Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
- + intent);
- return false;
- }
return true;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 9e7b46485d4c..5f3e50320752 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -17,11 +17,13 @@
package com.android.server.om;
import static android.app.AppGlobals.getPackageManager;
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REASON;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
@@ -356,7 +358,11 @@ public final class OverlayManagerService extends SystemService {
}
break;
case ACTION_PACKAGE_CHANGED:
- onPackageChanged(packageName, userIds);
+ // ignore the intent if it was sent by the package manager as a result of the
+ // overlay manager having sent ACTION_OVERLAY_CHANGED
+ if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
+ onPackageChanged(packageName, userIds);
+ }
break;
case ACTION_PACKAGE_REMOVED:
if (replacing) {
@@ -885,7 +891,7 @@ public final class OverlayManagerService extends SystemService {
FgThread.getHandler().post(() -> {
updateAssets(userId, targetPackageName);
- final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+ final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
Uri.fromParts("package", targetPackageName, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index 2371b04b583c..c3ed7d7dd6c6 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -21,7 +21,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.ISchedulingPolicyService;
import android.os.Process;
-import android.os.RemoteException;
import android.util.Log;
import com.android.server.SystemServerInitThreadPool;
@@ -64,7 +63,7 @@ public class SchedulingPolicyService extends ISchedulingPolicyService.Stub {
// (Note that if mediaserver thinks we're in boosted state before the crash,
// the state could go out of sync temporarily until mediaserver enables/disable
// boost next time, but this won't be a big issue.)
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
synchronized (mDeathRecipient) {
// only do this if we haven't already got a request to boost.
if (mBoostedPid == -1) {
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
new file mode 100644
index 000000000000..502f1e852e08
--- /dev/null
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsUsbTests"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index eb2e31d32beb..05b61689fdcf 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -195,19 +195,19 @@ public class AppsFilter {
return false;
}
for (Intent intent : querying.mQueriesIntents) {
- if (matches(intent, potentialTarget.providers, potentialTarget.activities,
- potentialTarget.services, potentialTarget.receivers)) {
+ if (matches(intent, potentialTarget)) {
return true;
}
}
return false;
}
- private static boolean matches(Intent intent,
- ArrayList<PackageParser.Provider> providerList,
- ArrayList<? extends Component<? extends IntentInfo>>... componentLists) {
- for (int p = providerList.size() - 1; p >= 0; p--) {
- PackageParser.Provider provider = providerList.get(p);
+ private static boolean matches(Intent intent, PackageParser.Package potentialTarget) {
+ for (int p = potentialTarget.providers.size() - 1; p >= 0; p--) {
+ PackageParser.Provider provider = potentialTarget.providers.get(p);
+ if (!provider.info.exported) {
+ continue;
+ }
final ProviderInfo providerInfo = provider.info;
final Uri data = intent.getData();
if ("content".equalsIgnoreCase(intent.getScheme())
@@ -216,19 +216,44 @@ public class AppsFilter {
return true;
}
}
+ for (int s = potentialTarget.services.size() - 1; s >= 0; s--) {
+ PackageParser.Service service = potentialTarget.services.get(s);
+ if (!service.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, service)) {
+ return true;
+ }
+ }
+ for (int a = potentialTarget.activities.size() - 1; a >= 0; a--) {
+ PackageParser.Activity activity = potentialTarget.activities.get(a);
+ if (!activity.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, activity)) {
+ return true;
+ }
+ }
+ for (int r = potentialTarget.receivers.size() - 1; r >= 0; r--) {
+ PackageParser.Activity receiver = potentialTarget.receivers.get(r);
+ if (!receiver.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, receiver)) {
+ return true;
+ }
+ }
+ return false;
+ }
- for (int l = componentLists.length - 1; l >= 0; l--) {
- ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l];
- for (int c = components.size() - 1; c >= 0; c--) {
- Component<? extends IntentInfo> component = components.get(c);
- ArrayList<? extends IntentInfo> intents = component.intents;
- for (int i = intents.size() - 1; i >= 0; i--) {
- IntentFilter intentFilter = intents.get(i);
- if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
- intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
- return true;
- }
- }
+ private static boolean matchesAnyFilter(
+ Intent intent, Component<? extends IntentInfo> component) {
+ ArrayList<? extends IntentInfo> intents = component.intents;
+ for (int i = intents.size() - 1; i >= 0; i--) {
+ IntentFilter intentFilter = intents.get(i);
+ if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
+ intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
+ return true;
}
}
return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b1d05f4c07d6..67d0f62021c2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1675,7 +1675,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Send broadcasts
for (int i = 0; i < size; i++) {
- sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]);
+ sendPackageChangedBroadcast(packages[i], true, components[i], uids[i],
+ null);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
@@ -2163,7 +2164,7 @@ public class PackageManagerService extends IPackageManager.Stub
// send broadcast that all consumers of the static shared library have changed
sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/,
new ArrayList<>(Collections.singletonList(pkg.packageName)),
- pkg.applicationInfo.uid);
+ pkg.applicationInfo.uid, null);
}
}
@@ -3134,7 +3135,7 @@ public class PackageManagerService extends IPackageManager.Stub
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
true /* onlyCoreApps */);
- mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> {
+ mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
traceLog.traceBegin("AppDataFixup");
@@ -17509,7 +17510,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
- libraryInfo, 0, currUserId);
+ libraryInfo, MATCH_KNOWN_PACKAGES, currUserId);
if (!ArrayUtils.isEmpty(libClientPackages)) {
Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+ " hosting lib " + libraryInfo.getName() + " version "
@@ -20039,7 +20040,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (sendNow) {
int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
- (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
+ (flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -20071,7 +20072,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void sendPackageChangedBroadcast(String packageName,
- boolean killFlag, ArrayList<String> componentNames, int packageUid) {
+ boolean killFlag, ArrayList<String> componentNames, int packageUid,
+ String reason) {
if (DEBUG_INSTALL)
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
@@ -20082,6 +20084,9 @@ public class PackageManagerService extends IPackageManager.Stub
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
+ if (reason != null) {
+ extras.putString(Intent.EXTRA_REASON, reason);
+ }
// If this is not reporting a change of the overall package, then only send it
// to registered receivers. We don't want to launch a swath of apps for every
// little component state change.
@@ -20329,6 +20334,34 @@ public class PackageManagerService extends IPackageManager.Stub
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
+ IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
+ overlayFilter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ Uri data = intent.getData();
+ if (data == null) {
+ return;
+ }
+ String packageName = data.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return;
+ }
+ sendPackageChangedBroadcast(pkg.packageName,
+ false /* killFlag */,
+ new ArrayList<>(Collections.singletonList(pkg.packageName)),
+ pkg.applicationInfo.uid,
+ Intent.ACTION_OVERLAY_CHANGED);
+ }
+ }, overlayFilter);
+
mModuleInfoProvider.systemReady();
// Installer service might attempt to install some packages that have been staged for
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 036d1e807f97..323c957acdd0 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -449,7 +449,14 @@ class UserSystemPackageInstaller {
}
void dump(PrintWriter pw) {
- for (int i = 0; i < mWhitelitsedPackagesForUserTypes.size(); i++) {
+ pw.print("Whitelisted packages per user type");
+ final int size = mWhitelitsedPackagesForUserTypes.size();
+ if (size == 0) {
+ pw.println(": N/A");
+ return;
+ }
+ pw.println(" (" + size + " packages)");
+ for (int i = 0; i < size; i++) {
final String pkgName = mWhitelitsedPackagesForUserTypes.keyAt(i);
final String whitelistedUserTypes =
UserInfo.flagsToString(mWhitelitsedPackagesForUserTypes.valueAt(i));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 750fc6832627..9b9dc88827a7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -63,7 +63,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -3321,29 +3320,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
- final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
- final ActivityOptions activityOptions = safeOptions != null
- ? safeOptions.getOptions(mStackSupervisor)
- : null;
- if (activityOptions == null
- || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
- || activityOptions.getCustomInPlaceResId() == 0) {
- throw new IllegalArgumentException("Expected in-place ActivityOption " +
- "with valid animation");
- }
- // Get top display of front most application.
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack != null) {
- final DisplayContent dc = focusedStack.getDisplay().mDisplayContent;
- dc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
- dc.mAppTransition.overrideInPlaceAppTransition(activityOptions.getPackageName(),
- activityOptions.getCustomInPlaceResId());
- dc.executeAppTransition();
- }
- }
-
- @Override
public void removeStack(int stackId) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 93c461f3add9..394b47520492 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -33,7 +33,6 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
@@ -2257,8 +2256,7 @@ public class AppTransition implements Dump {
static boolean isTaskTransit(int transit) {
return isTaskOpenTransit(transit)
|| transit == TRANSIT_TASK_CLOSE
- || transit == TRANSIT_TASK_TO_BACK
- || transit == TRANSIT_TASK_IN_PLACE;
+ || transit == TRANSIT_TASK_TO_BACK;
}
private static boolean isTaskOpenTransit(int transit) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 20a871baada4..894dfd4065a4 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -31,7 +31,6 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPE
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
@@ -179,8 +178,6 @@ public class AppTransitionController {
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
- processApplicationsAnimatingInPlace(transit);
-
handleClosingApps(transit, animLp, voiceInteraction);
handleOpeningApps(transit, animLp, voiceInteraction);
handleChangingApps(transit, animLp, voiceInteraction);
@@ -710,19 +707,4 @@ public class AppTransitionController {
}
return topApp;
}
-
- private void processApplicationsAnimatingInPlace(int transit) {
- if (transit == TRANSIT_TASK_IN_PLACE) {
- // Find the focused window
- final WindowState win = mDisplayContent.findFocusedWindow();
- if (win != null) {
- final AppWindowToken wtoken = win.mAppToken;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now animating app in place %s", wtoken);
- wtoken.cancelAnimation();
- wtoken.applyAnimationLocked(null, transit, false, false);
- wtoken.updateReportedVisibilityLocked();
- wtoken.showAllWindowsLocked();
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ac910cde79b0..4c3611e971b4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5152,7 +5152,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Let surface flinger to set the display ID of this input window handle because we don't
// know which display the parent surface control is on.
final InputWindowHandle portalWindowHandle = new InputWindowHandle(
- null /* inputApplicationHandle */, null /* clientWindow */, INVALID_DISPLAY);
+ null /* inputApplicationHandle */, INVALID_DISPLAY);
portalWindowHandle.name = name;
portalWindowHandle.token = new Binder();
portalWindowHandle.layoutParamsFlags =
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7be4dbd99766..150ae7906510 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,13 +16,12 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.TYPE_BUILT_IN;
@@ -3130,9 +3129,10 @@ public class DisplayPolicy {
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
mService.getStackBounds(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
- mService.getStackBounds(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
+ mService.getStackBounds(mDockedStackBounds.isEmpty()
+ ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
final Pair<Integer, Boolean> result =
updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int visibility = result.first;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 34820acfeccc..abd7222feea2 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -263,7 +263,7 @@ class DragState {
InputChannel[] channels = InputChannel.openInputChannelPair("drag");
mServerChannel = channels[0];
mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
@@ -272,7 +272,7 @@ class DragState {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.token = mServerChannel.getToken();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 8dae0163b612..ebe9f08f83b7 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -65,14 +65,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
} else {
mClientChannel = channels[1];
}
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
+ mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
mWindowHandle.token = mServerChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index ec36a820f5fe..eb9b3e92e21a 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -5,20 +5,25 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Slog;
+import android.view.IWindow;
import android.view.KeyEvent;
import android.view.WindowManager;
import com.android.server.input.InputManagerService;
import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicReference;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
private final WindowManagerService mService;
// Set to true when the first input device configuration change notification
@@ -37,6 +42,13 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
// which point the ActivityManager will enable dispatching.
private boolean mInputDispatchEnabled;
+ // TODO(b/141749603)) investigate if this can be part of client focus change dispatch
+ // Tracks the currently focused window used to update pointer capture state in clients
+ private AtomicReference<IWindow> mFocusedWindow = new AtomicReference<>();
+
+ // Tracks focused window pointer capture state
+ private boolean mFocusedWindowHasCapture;
+
public InputManagerCallback(WindowManagerService service) {
mService = service;
}
@@ -53,7 +65,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
}
synchronized (mService.mGlobalLock) {
- WindowState windowState = mService.windowForClientLocked(null, token, false);
+ WindowState windowState = mService.mInputToWindowMap.get(token);
if (windowState != null) {
Slog.i(TAG_WM, "WINDOW DIED " + windowState);
windowState.removeIfPossible();
@@ -72,9 +84,10 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
+ //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
synchronized (mService.mGlobalLock) {
if (token != null) {
- windowState = mService.windowForClientLocked(null, token, false);
+ windowState = mService.mInputToWindowMap.get(token);
if (windowState != null) {
appWindowToken = windowState.mAppToken;
}
@@ -109,7 +122,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
- (windowState != null) ? windowState.mSession.mPid : -1);
+ windowState.mSession.mPid);
if (!abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
@@ -239,6 +252,59 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
}
+ @Override
+ public boolean notifyFocusChanged(IBinder oldToken, IBinder newToken) {
+ boolean requestRefreshConfiguration = false;
+ final IWindow newFocusedWindow;
+ final WindowState win;
+
+ // TODO(b/141749603) investigate if this can be part of client focus change dispatch
+ synchronized (mService.mGlobalLock) {
+ win = mService.mInputToWindowMap.get(newToken);
+ }
+ newFocusedWindow = (win != null) ? win.mClient : null;
+
+ final IWindow focusedWindow = mFocusedWindow.get();
+ if (focusedWindow != null) {
+ if (focusedWindow.asBinder() == newFocusedWindow.asBinder()) {
+ Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
+ + focusedWindow);
+ return false;
+ }
+ requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false);
+ }
+ mFocusedWindow.set(newFocusedWindow);
+ return requestRefreshConfiguration;
+ }
+
+ @Override
+ public boolean requestPointerCapture(IBinder windowToken, boolean enabled) {
+ final IWindow focusedWindow = mFocusedWindow.get();
+ if (focusedWindow == null || focusedWindow.asBinder() != windowToken) {
+ Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
+ + windowToken);
+ return false;
+ }
+ if (mFocusedWindowHasCapture == enabled) {
+ Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
+ return false;
+ }
+ return dispatchPointerCaptureChanged(focusedWindow, enabled);
+ }
+
+ private boolean dispatchPointerCaptureChanged(IWindow focusedWindow, boolean enabled) {
+ if (mFocusedWindowHasCapture != enabled) {
+ mFocusedWindowHasCapture = enabled;
+ try {
+ focusedWindow.dispatchPointerCaptureChanged(enabled);
+ } catch (RemoteException ex) {
+ /* ignore */
+ }
+ return true;
+ }
+ return false;
+ }
+
/** 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/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 932b4fac4592..dc9a598e2a81 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -411,7 +411,7 @@ final class InputMonitor {
WallpaperController wallpaperController;
// An invalid window handle that tells SurfaceFlinger not update the input info.
- final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
+ final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId);
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cc55e0137e20..3731d3f38800 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -241,6 +241,10 @@ class InsetsSourceProvider {
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
+ // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
+ t.setAlpha(animationLeash, 1 /* alpha */);
+ t.hide(animationLeash);
+
mCapturedLeash = animationLeash;
final Rect frame = mWin.getWindowFrames().mFrame;
t.setPosition(mCapturedLeash, frame.left, frame.top);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 94d010e846f5..c59a73b217ad 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -20,7 +20,7 @@ import static android.view.SurfaceControl.HIDDEN;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Binder;
+import android.os.IBinder;
import android.os.Process;
import android.view.InputChannel;
import android.view.InputEventReceiver;
@@ -170,7 +170,7 @@ public class Letterbox {
final InputWindowHandle mWindowHandle;
final InputEventReceiver mInputEventReceiver;
final WindowManagerService mWmService;
- final Binder mToken = new Binder();
+ final IBinder mToken;
InputInterceptor(String namePrefix, WindowState win) {
mWmService = win.mWmService;
@@ -180,10 +180,11 @@ public class Letterbox {
mClientChannel = channels[1];
mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
- mWmService.mInputManager.registerInputChannel(mServerChannel, mToken);
+ mWmService.mInputManager.registerInputChannel(mServerChannel);
+ mToken = mServerChannel.getToken();
mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
- null /* clientWindow */, win.getDisplayId());
+ win.getDisplayId());
mWindowHandle.name = name;
mWindowHandle.token = mToken;
mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index b680fa45f00f..478b1b533f4c 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -256,7 +256,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
mServerChannel = channels[0];
mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mInputEventReceiver = new WindowPositionerEventReceiver(
mClientChannel, mService.mAnimationHandler.getLooper(),
@@ -267,8 +267,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
- display.getDisplayId());
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = mService.getDragLayerLocked();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6f9f2c0f9708..0287270c7014 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -518,7 +518,10 @@ public class WindowManagerService extends IWindowManager.Stub
final ArraySet<Session> mSessions = new ArraySet<>();
/** Mapping from an IWindow IBinder to the server's Window object. */
- final WindowHashMap mWindowMap = new WindowHashMap();
+ final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
+
+ /** Mapping from an InputWindowHandle token to the server's Window object. */
+ final HashMap<IBinder, WindowState> mInputToWindowMap = new HashMap<>();
/** Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock;
@@ -1578,7 +1581,6 @@ public class WindowManagerService extends IWindowManager.Stub
win.attach();
mWindowMap.put(client.asBinder(), win);
-
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
@@ -7592,7 +7594,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
- final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
+ final WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
return;
}
@@ -7665,9 +7667,9 @@ public class WindowManagerService extends IWindowManager.Stub
clientChannel.transferTo(outInputChannel);
clientChannel.dispose();
- mInputManager.registerInputChannel(inputChannel, null /* generate new token */);
+ mInputManager.registerInputChannel(inputChannel);
- InputWindowHandle h = new InputWindowHandle(null, null, displayId);
+ InputWindowHandle h = new InputWindowHandle(null, displayId);
h.token = inputChannel.getToken();
h.name = name;
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 56e08b2843b5..40bb0597bfd1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -820,8 +820,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastRequestedHeight = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
- mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
- getDisplayId());
+ mAppToken != null ? mAppToken.mInputApplicationHandle : null, getDisplayId());
}
void attach() {
@@ -2191,7 +2190,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
- mInputWindowHandle.token = mClient.asBinder();
+ mWmService.mInputManager.registerInputChannel(mInputChannel);
+ mClientChannel.setToken(mInputChannel.getToken());
+ mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
@@ -2202,7 +2203,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
- mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
+ mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
void disposeInputChannel() {
@@ -2223,6 +2224,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mClientChannel = null;
}
mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
+ mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 7f68c48e473f..0b4ea9927139 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -38,6 +38,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.WindowContentFrameStats;
+import android.view.WindowManager;
import com.android.server.protolog.common.ProtoLog;
@@ -108,6 +109,13 @@ class WindowSurfaceController {
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, ownerUid);
+
+ if ((win.getAttrs().privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0) {
+ b.setContainerLayer();
+ }
+
+
mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 47291cb286ba..9569ac8dfdd1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2186,7 +2186,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
void postOnSystemServerInitThreadPool(Runnable runnable) {
- SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+ SystemServerInitThreadPool.submit(runnable, LOG_TAG);
}
public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a453164b8fbc..4cf98d32f8de 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -635,7 +635,7 @@ public final class SystemServer {
Slog.i(TAG, "Reading configuration...");
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
t.traceBegin(TAG_SYSTEM_CONFIG);
- SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
+ SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
t.traceEnd();
// Platform compat service is used by ActivityManagerService, PackageManagerService, and
@@ -821,7 +821,7 @@ public final class SystemServer {
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
// before using it.
- mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {
+ mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_SENSOR_SERVICE);
startSensorService();
@@ -946,7 +946,7 @@ public final class SystemServer {
// ensure that it completes before the 32 bit relro process is forked
// from the zygote. In the event that it takes too long, the webview
// RELRO process will block, but it will do so without holding any locks.
- mZygotePreload = SystemServerInitThreadPool.get().submit(() -> {
+ mZygotePreload = SystemServerInitThreadPool.submit(() -> {
try {
Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
@@ -1058,7 +1058,7 @@ public final class SystemServer {
// Start receiving calls from HIDL services. Start in in a separate thread
// because it need to connect to SensorManager. This have to start
// after START_SENSOR_SERVICE is done.
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_HIDL_SERVICES);
startHidlServices();
@@ -2050,7 +2050,7 @@ public final class SystemServer {
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
Future<?> webviewPrep = null;
if (!mOnlyCore && mWebViewUpdateService != null) {
- webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
+ webviewPrep = SystemServerInitThreadPool.submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(WEBVIEW_PREPARATION);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 2aa625a657ef..9c9730501a78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -17,6 +17,7 @@ package com.android.server;
import static androidx.test.InstrumentationRegistry.getContext;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
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.inOrder;
@@ -31,6 +32,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -51,6 +53,7 @@ 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.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.atLeastOnce;
@@ -72,6 +75,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -87,11 +91,13 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
/**
* Tests for {@link com.android.server.DeviceIdleController}.
@@ -99,6 +105,7 @@ import org.mockito.quality.Strictness;
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
private DeviceIdleController mDeviceIdleController;
+ private DeviceIdleController.MyHandler mHandler;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
private DeviceIdleController.Constants mConstants;
@@ -112,8 +119,6 @@ public class DeviceIdleControllerTest {
@Mock
private ContentResolver mContentResolver;
@Mock
- private DeviceIdleController.MyHandler mHandler;
- @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -171,6 +176,23 @@ public class DeviceIdleControllerTest {
@Override
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
+ if (mHandler == null) {
+ mHandler = controller.new MyHandler(getContext().getMainLooper());
+ spyOn(mHandler);
+ doNothing().when(mHandler).handleMessage(argThat((message) ->
+ message.what != MSG_REPORT_STATIONARY_STATUS));
+ doAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Message msg = invocation.getArgument(0);
+ mHandler.handleMessage(msg);
+ return true;
+ }
+ }).when(mHandler).sendMessageDelayed(
+ argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+ anyLong());
+ }
+
return mHandler;
}
@@ -236,6 +258,19 @@ public class DeviceIdleControllerTest {
}
}
+ private class StationaryListenerForTest implements DeviceIdleInternal.StationaryListener {
+ boolean motionExpected = false;
+ boolean isStationary = false;
+
+ @Override
+ public void onDeviceStationaryChanged(boolean isStationary) {
+ if (isStationary == motionExpected) {
+ fail("Unexpected device stationary status: " + isStationary);
+ }
+ this.isStationary = isStationary;
+ }
+ }
+
@Before
public void setUp() {
mMockingSession = mockitoSession()
@@ -265,8 +300,6 @@ public class DeviceIdleControllerTest {
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
- mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
- doNothing().when(mHandler).handleMessage(any());
mInjector = new InjectorForTest(getContext());
doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
@@ -1724,6 +1757,86 @@ public class DeviceIdleControllerTest {
1.0f, curfactor, delta);
}
+ @Test
+ public void testStationaryDetection_QuickDozeOff() {
+ setQuickDozeEnabled(false);
+ enterDeepState(STATE_IDLE);
+ // Regular progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
+ + mConstants.LOCATING_TIMEOUT;
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ assertTrue(stationaryListener.isStationary);
+
+ // Test motion
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onTrigger(null);
+ assertFalse(stationaryListener.isStationary);
+ }
+
+ @Test
+ public void testStationaryDetection_QuickDozeOn() {
+ setAlarmSoon(false);
+ enterDeepState(STATE_QUICK_DOZE_DELAY);
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+ // Quick doze progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
+ alarmListener.capture(), any());
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+ assertFalse(stationaryListener.isStationary);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onSensorChanged(null);
+ assertFalse(stationaryListener.isStationary);
+
+ // Since we're in quick doze, the device shouldn't stop idling.
+ verifyStateConditions(STATE_IDLE);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 43f251a9281f..d11d98766b01 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -156,7 +156,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock private WindowManagerInternal mMockWindowManagerInternal;
- @Mock private GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private IBinder mMockService;
@Mock private IAccessibilityServiceClient mMockServiceInterface;
@Mock private KeyEventDispatcher mMockKeyEventDispatcher;
@@ -221,7 +221,7 @@ public class AbstractAccessibilityServiceConnectionTest {
mServiceConnection = new TestAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
- mMockSystemSupport, mMockWindowManagerInternal, mMockGlobalActionPerformer,
+ mMockSystemSupport, mMockWindowManagerInternal, mMockSystemActionPerformer,
mMockA11yWindowManager);
// Assume that the service is connected
mServiceConnection.mService = mMockService;
@@ -489,7 +489,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void performGlobalAction() {
mServiceConnection.performGlobalAction(GLOBAL_ACTION_HOME);
- verify(mMockGlobalActionPerformer).performGlobalAction(GLOBAL_ACTION_HOME);
+ verify(mMockSystemActionPerformer).performSystemAction(GLOBAL_ACTION_HOME);
}
@Test
@@ -776,10 +776,10 @@ public class AbstractAccessibilityServiceConnectionTest {
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
a11yWindowManager);
mResolvedUserId = USER_ID;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 6be5a3785865..edf82ee30e2d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -75,7 +75,7 @@ public class AccessibilityServiceConnectionTest {
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
@Mock MagnificationController mMockMagnificationController;
@Mock IBinder mMockIBinder;
@@ -104,7 +104,7 @@ public class AccessibilityServiceConnectionTest {
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
- mMockGlobalActionPerformer, mMockA11yWindowManager);
+ mMockSystemActionPerformer, mMockA11yWindowManager);
when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index c73be6f100cd..37f5b87ac115 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -1,17 +1,17 @@
/*
- ** 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.
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.accessibility;
@@ -35,13 +35,11 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.Consumer;
-
/**
- * Tests for GlobalActionPerformer
+ * Tests for SystemActionPerformer
*/
-public class GlobalActionPerformerTest {
- GlobalActionPerformer mGlobalActionPerformer;
+public class SystemActionPerformerTest {
+ SystemActionPerformer mSystemActionPerformer;
@Mock Context mMockContext;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@@ -55,34 +53,34 @@ public class GlobalActionPerformerTest {
when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
.thenReturn(mMockStatusBarManager);
- mGlobalActionPerformer =
- new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
+ mSystemActionPerformer =
+ new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
() -> mMockScreenshotHelper);
}
@Test
public void testNotifications_expandsNotificationPanel() {
- mGlobalActionPerformer
- .performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+ mSystemActionPerformer
+ .performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
verify(mMockStatusBarManager).expandNotificationsPanel();
}
@Test
public void testQuickSettings_requestsQuickSettingsPanel() {
- mGlobalActionPerformer
- .performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
+ mSystemActionPerformer
+ .performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
verify(mMockStatusBarManager).expandSettingsPanel();
}
@Test
public void testPowerDialog_requestsFromWindowManager() {
- mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
verify(mMockWindowManagerInternal).showGlobalActions();
}
@Test
public void testScreenshot_requestsFromScreenshotHelper() {
- mGlobalActionPerformer.performGlobalAction(
+ mSystemActionPerformer.performSystemAction(
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
verify(mMockScreenshotHelper).takeScreenshot(
eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 210de538d0bd..deb6f71c695b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -64,7 +64,7 @@ public class UiAutomationManagerTest {
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock IBinder mMockOwner;
@Mock IAccessibilityServiceClient mMockAccessibilityServiceClient;
@Mock IBinder mMockServiceAsBinder;
@@ -174,7 +174,7 @@ public class UiAutomationManagerTest {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
mMessageCapturingHandler, mMockSecurityPolicy, mMockSystemSupport,
- mMockWindowManagerInternal, mMockGlobalActionPerformer,
+ mMockWindowManagerInternal, mMockSystemActionPerformer,
mMockA11yWindowManager, flags);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
new file mode 100644
index 000000000000..baf1ed00c7b5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.engine;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.OpenFormula;
+import com.android.server.integrity.model.Rule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleEvaluatorTest {
+
+ private static final String PACKAGE_NAME_1 = "com.test.app";
+ private static final String PACKAGE_NAME_2 = "com.test.app2";
+ private static final String APP_CERTIFICATE = "test_cert";
+ private static final AppInstallMetadata APP_INSTALL_METADATA =
+ new AppInstallMetadata.Builder()
+ .setPackageName(PACKAGE_NAME_1)
+ .setAppCertificate(APP_CERTIFICATE)
+ .build();
+
+ @Test
+ public void testMatchRules_emptyRules() {
+ List<Rule> rules = new ArrayList<>();
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_emptyMatch() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+
+ @Test
+ public void testMatchRules_oneMatch() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1), Rule.Effect.DENY);
+ Rule rule2 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule1, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_multipleMatches() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1), Rule.Effect.DENY);
+ OpenFormula openFormula2 = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule2 = new Rule(
+ openFormula2, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+ APP_INSTALL_METADATA);
+
+ assertNotEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleWithNot() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+ Collections.singletonList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2)));
+ Rule rule = new Rule(openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleWithIntegerOperators() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.GT,
+ 1), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule1, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_validForm() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleNotInDNF() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_openFormulaWithNot() {
+ OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+ Collections.singletonList(openSubFormula));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
index d1fa0f97b071..048ee707d8fe 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
@@ -19,6 +19,7 @@ package com.android.server.integrity.model;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
@@ -83,4 +84,20 @@ public class RuleTest {
assertEquals(String.format("Rule: PACKAGE_NAME EQ %s AND APP_CERTIFICATE EQ %s, DENY",
PACKAGE_NAME, APP_CERTIFICATE), toString);
}
+
+ @Test
+ public void testEquals_trueCase() {
+ Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+ Rule rule2 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+
+ assertEquals(rule1, rule2);
+ }
+
+ @Test
+ public void testEquals_falseCase() {
+ Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+ Rule rule2 = new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT);
+
+ assertNotEquals(rule1, rule2);
+ }
}
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index 9775abd5075c..cea83230391d 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -19,6 +19,8 @@ package android.telephony;
import android.annotation.NonNull;
import android.os.Parcel;
+import dalvik.annotation.codegen.CovariantReturnType;
+
import java.util.Objects;
/**
@@ -46,6 +48,7 @@ public final class CellInfoNr extends CellInfo {
/**
* @return a {@link CellIdentityNr} instance.
*/
+ @CovariantReturnType(returnType = CellIdentityNr.class, presentAfter = 29)
@Override
@NonNull
public CellIdentity getCellIdentity() {
@@ -55,6 +58,7 @@ public final class CellInfoNr extends CellInfo {
/**
* @return a {@link CellSignalStrengthNr} instance.
*/
+ @CovariantReturnType(returnType = CellSignalStrengthNr.class, presentAfter = 29)
@Override
@NonNull
public CellSignalStrength getCellSignalStrength() {
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index ee3c1b1be6d9..60732a3db59a 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,7 +16,6 @@
package android.telephony;
-import android.os.ParcelableException;
import android.telephony.CellInfo;
import java.util.List;
@@ -28,5 +27,5 @@ import java.util.List;
oneway interface ICellInfoCallback
{
void onCellInfo(in List<CellInfo> state);
- void onError(in int errorCode, in ParcelableException detail);
+ void onError(in int errorCode, in String exceptionName, in String message);
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index f4330fa0b725..2d35f8eae816 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1814,6 +1814,36 @@ public final class SmsManager {
// SMS send failure result codes
+ /** @hide */
+ @IntDef(prefix = { "RESULT" }, value = {
+ RESULT_ERROR_NONE,
+ RESULT_ERROR_GENERIC_FAILURE,
+ RESULT_ERROR_RADIO_OFF,
+ RESULT_ERROR_NULL_PDU,
+ RESULT_ERROR_NO_SERVICE,
+ RESULT_ERROR_LIMIT_EXCEEDED,
+ RESULT_ERROR_FDN_CHECK_FAILURE,
+ RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
+ RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
+ RESULT_RADIO_NOT_AVAILABLE,
+ RESULT_NETWORK_REJECT,
+ RESULT_INVALID_ARGUMENTS,
+ RESULT_INVALID_STATE,
+ RESULT_NO_MEMORY,
+ RESULT_INVALID_SMS_FORMAT,
+ RESULT_SYSTEM_ERROR,
+ RESULT_MODEM_ERROR,
+ RESULT_NETWORK_ERROR,
+ RESULT_INVALID_SMSC_ADDRESS,
+ RESULT_OPERATION_NOT_ALLOWED,
+ RESULT_INTERNAL_ERROR,
+ RESULT_NO_RESOURCES,
+ RESULT_CANCELLED,
+ RESULT_REQUEST_NOT_SUPPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {}
+
/**
* No error.
* @hide
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1b876575a742..e288f25315af 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2101,13 +2101,13 @@ public class SubscriptionManager {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
- return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getMaxPhoneCount();
+ return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSupportedModemCount();
}
/** @hide */
@UnsupportedAppUsage
public static boolean isValidPhoneId(int phoneId) {
- return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getMaxPhoneCount();
+ return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getSupportedModemCount();
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2442023ee27c..03e57e728610 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -65,7 +65,6 @@ import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.DataState;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
@@ -283,6 +282,21 @@ public class TelephonyManager {
};
/** @hide */
+ @IntDef(prefix = {"MODEM_COUNT_"},
+ value = {
+ MODEM_COUNT_NO_MODEM,
+ MODEM_COUNT_SINGLE_MODEM,
+ MODEM_COUNT_DUAL_MODEM,
+ MODEM_COUNT_TRI_MODEM
+ })
+ public @interface ModemCount {}
+
+ public static final int MODEM_COUNT_NO_MODEM = 0;
+ public static final int MODEM_COUNT_SINGLE_MODEM = 1;
+ public static final int MODEM_COUNT_DUAL_MODEM = 2;
+ public static final int MODEM_COUNT_TRI_MODEM = 3;
+
+ /** @hide */
@UnsupportedAppUsage
public TelephonyManager(Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
@@ -359,12 +373,26 @@ public class TelephonyManager {
/**
* Returns the number of phones available.
* Returns 0 if none of voice, sms, data is not supported
- * Returns 1 for Single standby mode (Single SIM functionality)
- * Returns 2 for Dual standby mode.(Dual SIM functionality)
- * Returns 3 for Tri standby mode.(Tri SIM functionality)
+ * Returns 1 for Single standby mode (Single SIM functionality).
+ * Returns 2 for Dual standby mode (Dual SIM functionality).
+ * Returns 3 for Tri standby mode (Tri SIM functionality).
+ * @deprecated Use {@link #getActiveModemCount} instead.
*/
+ @Deprecated
public int getPhoneCount() {
- int phoneCount = 1;
+ return getActiveModemCount();
+ }
+
+ /**
+ * Returns the number of logical modems currently configured to be activated.
+ *
+ * Returns 0 if none of voice, sms, data is not supported
+ * Returns 1 for Single standby mode (Single SIM functionality).
+ * Returns 2 for Dual standby mode (Dual SIM functionality).
+ * Returns 3 for Tri standby mode (Tri SIM functionality).
+ */
+ public @ModemCount int getActiveModemCount() {
+ int modemCount = 1;
switch (getMultiSimConfiguration()) {
case UNKNOWN:
ConnectivityManager cm = mContext == null ? null : (ConnectivityManager) mContext
@@ -372,33 +400,30 @@ public class TelephonyManager {
// check for voice and data support, 0 if not supported
if (!isVoiceCapable() && !isSmsCapable() && cm != null
&& !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
- phoneCount = 0;
+ modemCount = MODEM_COUNT_NO_MODEM;
} else {
- phoneCount = 1;
+ modemCount = MODEM_COUNT_SINGLE_MODEM;
}
break;
case DSDS:
case DSDA:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+ modemCount = MODEM_COUNT_DUAL_MODEM;
break;
case TSTS:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_TRI_SIM;
+ modemCount = MODEM_COUNT_TRI_MODEM;
break;
}
- return phoneCount;
+ return modemCount;
}
/**
- *
- * Return how many phone / logical modem can be active simultaneously, in terms of device
+ * Return how many logical modem can be potentially active simultaneously, in terms of hardware
* capability.
- * For example, for a dual-SIM capable device, it always returns 2, even if only one logical
- * modem / SIM is active (aka in single SIM mode).
- *
- * TODO: b/139642279 publicize and rename.
- * @hide
+ * It might return different value from {@link #getActiveModemCount}. For example, for a
+ * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on),
+ * {@link #getActiveModemCount} returns 1 while this API returns 2.
*/
- public int getMaxPhoneCount() {
+ public @ModemCount int getSupportedModemCount() {
// TODO: b/139642279 when turning on this feature, remove dependency of
// PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE and always return result based on
// PROPERTY_MAX_ACTIVE_MODEMS.
@@ -5545,18 +5570,20 @@ public class TelephonyManager {
telephony.requestCellInfoUpdate(
getSubId(),
new ICellInfoCallback.Stub() {
+ @Override
public void onCellInfo(List<CellInfo> cellInfo) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
- public void onError(int errorCode, android.os.ParcelableException detail) {
+ @Override
+ public void onError(int errorCode, String exceptionName, String message) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onError(
- errorCode, detail.getCause())));
+ errorCode,
+ createThrowableByClassName(exceptionName, message))));
}
}, getOpPackageName());
-
} catch (RemoteException ex) {
}
}
@@ -5585,21 +5612,36 @@ public class TelephonyManager {
telephony.requestCellInfoUpdateWithWorkSource(
getSubId(),
new ICellInfoCallback.Stub() {
+ @Override
public void onCellInfo(List<CellInfo> cellInfo) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
- public void onError(int errorCode, android.os.ParcelableException detail) {
+ @Override
+ public void onError(int errorCode, String exceptionName, String message) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onError(
- errorCode, detail.getCause())));
+ errorCode,
+ createThrowableByClassName(exceptionName, message))));
}
}, getOpPackageName(), workSource);
} catch (RemoteException ex) {
}
}
+ private static Throwable createThrowableByClassName(String className, String message) {
+ if (className == null) {
+ return null;
+ }
+ try {
+ Class<?> c = Class.forName(className);
+ return (Throwable) c.getConstructor(String.class).newInstance(message);
+ } catch (ReflectiveOperationException | ClassCastException e) {
+ }
+ return new RuntimeException(className + ": " + message);
+ }
+
/**
* Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
* PhoneStateListener.onCellInfoChanged} will be invoked.
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index a1a7fcc5dd51..2fad8479178e 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -183,19 +183,17 @@ public class ImsMmTelManager {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
- * @param imsTransportType the radio access technology. Valid values are defined in
- * {@link android.telephony.AccessNetworkConstants.TransportType}.
+ * @param imsTransportType the radio access technology.
*/
- public void onRegistered(int imsTransportType) {
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsTransportType the radio access technology. Valid values are defined in
- * {@link android.telephony.AccessNetworkConstants.TransportType}.
+ * @param imsTransportType the radio access technology.
*/
- public void onRegistering(int imsTransportType) {
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
@@ -207,15 +205,14 @@ public class ImsMmTelManager {
}
/**
- * A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
+ * A failure has occurred when trying to handover registration to another technology type.
*
- * @param imsTransportType The
- * {@link android.telephony.AccessNetworkConstants.TransportType}
- * transport type that has failed to handover registration to.
+ * @param imsTransportType The transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- public void onTechnologyChangeFailed(int imsTransportType, @Nullable ImsReasonInfo info) {
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @Nullable ImsReasonInfo info) {
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 175769bd34e4..36ece958d501 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -17,6 +17,7 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.SmsManager;
@@ -148,14 +149,16 @@ public class ImsSmsImplBase {
*
* @param token unique token generated by the platform that should be used when triggering
* callbacks for this specific message.
- * @param messageRef the message reference.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
* @param smsc the Short Message Service Center address.
* @param isRetry whether it is a retry of an already attempted message or not.
* @param pdu PDU representing the contents of the message.
*/
- public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SmsMessage.Format String format, String smsc, boolean isRetry,
byte[] pdu) {
// Base implementation returns error. Should be overridden.
try {
@@ -172,14 +175,13 @@ public class ImsSmsImplBase {
* provider.
*
* @param token token provided in {@link #onSmsReceived(int, String, byte[])}
- * @param messageRef the message reference
- * @param result result of delivering the message. Valid values are:
- * {@link #DELIVER_STATUS_OK},
- * {@link #DELIVER_STATUS_ERROR_GENERIC},
- * {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
- * {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
*/
- public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
+ public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @DeliverStatusResult int result) {
Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
}
@@ -191,12 +193,13 @@ public class ImsSmsImplBase {
*
* @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
* or {@link #onSmsStatusReportReceived(int, String, byte[])}
- * @param messageRef the message reference
- * @param result result of delivering the message. Valid values are:
- * {@link #STATUS_REPORT_STATUS_OK},
- * {@link #STATUS_REPORT_STATUS_ERROR}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
*/
- public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
+ public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @StatusReportResult int result) {
Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
}
@@ -210,12 +213,12 @@ public class ImsSmsImplBase {
* {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
* @param token unique token generated by IMS providers that the platform will use to trigger
* callbacks for this message.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param format the format of the message.
* @param pdu PDU representing the contents of the message.
* @throws RuntimeException if called before {@link #onReady()} is triggered.
*/
- public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
+ public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
+ throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -241,13 +244,16 @@ public class ImsSmsImplBase {
* sent successfully.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
*
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
*/
- public final void onSendSmsResultSuccess(int token, int messageRef) throws RuntimeException {
+ public final void onSendSmsResultSuccess(int token,
+ @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -266,34 +272,11 @@ public class ImsSmsImplBase {
* to the platform.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
* @param status result of sending the SMS.
- * @param reason reason in case status is failure. Valid values are:
- * {@link SmsManager#RESULT_ERROR_NONE},
- * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
- * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
- * {@link SmsManager#RESULT_ERROR_NULL_PDU},
- * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
- * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
- * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
- * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
- * {@link SmsManager#RESULT_NETWORK_REJECT},
- * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
- * {@link SmsManager#RESULT_INVALID_STATE},
- * {@link SmsManager#RESULT_NO_MEMORY},
- * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
- * {@link SmsManager#RESULT_SYSTEM_ERROR},
- * {@link SmsManager#RESULT_MODEM_ERROR},
- * {@link SmsManager#RESULT_NETWORK_ERROR},
- * {@link SmsManager#RESULT_ENCODING_ERROR},
- * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
- * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
- * {@link SmsManager#RESULT_INTERNAL_ERROR},
- * {@link SmsManager#RESULT_NO_RESOURCES},
- * {@link SmsManager#RESULT_CANCELLED},
- * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+ * @param reason reason in case status is failure.
*
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
@@ -303,8 +286,8 @@ public class ImsSmsImplBase {
* send result.
*/
@Deprecated
- public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
- int reason) throws RuntimeException {
+ public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -324,34 +307,10 @@ public class ImsSmsImplBase {
* network.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
* @param status result of sending the SMS.
- * @param reason Valid values are:
- * {@link SmsManager#RESULT_ERROR_NONE},
- * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
- * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
- * {@link SmsManager#RESULT_ERROR_NULL_PDU},
- * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
- * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
- * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
- * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
- * {@link SmsManager#RESULT_NETWORK_REJECT},
- * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
- * {@link SmsManager#RESULT_INVALID_STATE},
- * {@link SmsManager#RESULT_NO_MEMORY},
- * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
- * {@link SmsManager#RESULT_SYSTEM_ERROR},
- * {@link SmsManager#RESULT_MODEM_ERROR},
- * {@link SmsManager#RESULT_NETWORK_ERROR},
- * {@link SmsManager#RESULT_ENCODING_ERROR},
- * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
- * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
- * {@link SmsManager#RESULT_INTERNAL_ERROR},
- * {@link SmsManager#RESULT_NO_RESOURCES},
- * {@link SmsManager#RESULT_CANCELLED},
- * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
* @param networkErrorCode the error code reported by the carrier network if sending this SMS
* has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
* generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
@@ -360,9 +319,9 @@ public class ImsSmsImplBase {
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
*/
- public final void onSendSmsResultError(int token, int messageRef, @SendStatusResult int status,
- int reason, int networkErrorCode)
- throws RuntimeException {
+ public final void onSendSmsResultError(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
+ @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -384,9 +343,10 @@ public class ImsSmsImplBase {
* the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
* with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
* @param pdu PDU representing the content of the status report.
* @throws RuntimeException if called before {@link #onReady()} is triggered
*
@@ -394,7 +354,8 @@ public class ImsSmsImplBase {
* message reference.
*/
@Deprecated
- public final void onSmsStatusReportReceived(int token, int messageRef, String format,
+ public final void onSmsStatusReportReceived(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
byte[] pdu) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
@@ -419,13 +380,12 @@ public class ImsSmsImplBase {
* with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
* @param token unique token generated by IMS providers that the platform will use to trigger
* callbacks for this message.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param format the format of the message.
* @param pdu PDU representing the content of the status report.
* @throws RuntimeException if called before {@link #onReady()} is triggered
*/
- public final void onSmsStatusReportReceived(int token, String format, byte[] pdu)
- throws RuntimeException {
+ public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
+ byte[] pdu) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -450,13 +410,11 @@ public class ImsSmsImplBase {
}
/**
- * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
- * Provider.
+ * Returns the SMS format that the ImsService expects.
*
- * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @return The expected format of the SMS messages.
*/
- public String getSmsFormat() {
+ public @SmsMessage.Format String getSmsFormat() {
return SmsMessage.FORMAT_3GPP;
}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index a95b6f11e98a..fcd4701c7630 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,6 +758,12 @@ public class MockContext extends Context {
/** {@hide} */
@Override
+ public Context createContextAsUser(UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
public int getUserId() {
throw new UnsupportedOperationException();
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bffbbfda08ee..cf3fba8bef91 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5709,7 +5709,6 @@ public class ConnectivityServiceTest {
}
@Test
- @FlakyTest(bugId = 140305678)
public void testTcpBufferReset() throws Exception {
final String testTcpBufferSizes = "1,2,3,4,5,6";
final NetworkRequest networkRequest = new NetworkRequest.Builder()