summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Mitchell <rtmitchell@google.com>2020-03-20 18:21:20 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-03-20 18:21:20 +0000
commitc5f968d58b839115c649374deb16190ee80151f6 (patch)
tree46a755cc01078200cd5f02f618237e203328acca
parent85ded38f799020756024bce6ef07fe10dde8ffa5 (diff)
parentb0544a733cc73522c7cef2a114eefe65552627f0 (diff)
Merge changes from topic "res_loader_dir" into rvc-dev am: b0544a733c
Change-Id: I18c28c7293a24d712940c9e80e7817f9547512f8
-rw-r--r--api/current.txt14
-rw-r--r--core/java/android/app/ResourcesManager.java10
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java2
-rw-r--r--core/java/android/content/res/ApkAssets.java286
-rw-r--r--core/java/android/content/res/AssetManager.java207
-rw-r--r--core/java/android/content/res/Resources.java46
-rw-r--r--core/java/android/content/res/loader/AssetsProvider.java20
-rw-r--r--core/java/android/content/res/loader/DirectoryAssetsProvider.java80
-rw-r--r--core/java/android/content/res/loader/ResourcesLoader.java22
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java142
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp275
-rw-r--r--core/jni/android_util_AssetManager.cpp12
-rw-r--r--core/tests/ResourceLoaderTests/Android.bp54
-rw-r--r--core/tests/ResourceLoaderTests/AndroidTest.xml2
-rw-r--r--core/tests/ResourceLoaderTests/NonAsset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/assets/Asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/assets/asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/assets/base_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_png.png (renamed from core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png)bin963 -> 963 bytes
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_xml.xml (renamed from core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/res/values/strings.xml23
-rw-r--r--core/tests/ResourceLoaderTests/res/values/values.xml (renamed from core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml)3
-rw-r--r--core/tests/ResourceLoaderTests/resources/Android.bp115
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml9
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml9
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestSplit1.xml (renamed from core/tests/ResourceLoaderTests/splits/SplitOne/AndroidManifest.xml)3
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestSplit2.xml (renamed from core/tests/ResourceLoaderTests/splits/SplitThree/AndroidManifest.xml)3
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestSplit3.xml (renamed from core/tests/ResourceLoaderTests/splits/SplitTwo/AndroidManifest.xml)5
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestSplit4.xml (renamed from core/tests/ResourceLoaderTests/splits/SplitFour/AndroidManifest.xml)3
-rwxr-xr-xcore/tests/ResourceLoaderTests/resources/compileAndLink.sh136
-rw-r--r--core/tests/ResourceLoaderTests/resources/framework/res/drawable-mdpi/ic_delete.png (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png)bin1445 -> 1445 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/framework/res/layout/activity_list_item.xml (renamed from core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/framework/res/values/public.xml (renamed from core/tests/ResourceLoaderTests/resources/res/values/string_four.xml)5
-rw-r--r--core/tests/ResourceLoaderTests/resources/framework/res/values/values.xml (renamed from core/tests/ResourceLoaderTests/resources/res/values/dimen_four.xml)3
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/assets/asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_png.png (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapRed.png)bin962 -> 962 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_xml.xml (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/res/layout/layout.xml (renamed from core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/res/values/values.xml (renamed from core/tests/ResourceLoaderTests/resources/res/values/string_three.xml)9
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/assets/asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_png.png (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png)bin146 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_xml.xml (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/res/layout/layout.xml (renamed from core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/res/values/values.xml24
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/assets/asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_png.png (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png)bin146 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_xml.xml (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableThree.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/res/layout/layout.xml (renamed from core/tests/ResourceLoaderTests/resources/res/layout/layout_three.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/res/values/values.xml24
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/assets/asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_png.png (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapWhite.png)bin958 -> 958 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_xml.xml (renamed from core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableFour.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/res/layout/layout.xml (renamed from core/tests/ResourceLoaderTests/resources/res/layout/layout_four.xml)0
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/res/values/values.xml24
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider_additional/res/values/values.xml (renamed from core/tests/ResourceLoaderTests/resources/res/values/dimen_three.xml)3
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider_stable/res/values/public.xml (renamed from core/tests/ResourceLoaderTests/splits/SplitFour/res/values/string_split.xml)9
-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_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/SplitFour/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitOne/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitOne/res/values/string_split.xml21
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitThree/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitThree/res/values/string_spli.xml21
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitTwo/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/SplitTwo/res/values/string_split.xml21
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt105
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt220
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt302
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt406
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt13
-rw-r--r--libs/androidfw/ApkAssets.cpp596
-rw-r--r--libs/androidfw/Asset.cpp25
-rw-r--r--libs/androidfw/AssetManager2.cpp6
-rw-r--r--libs/androidfw/LoadedArsc.cpp2
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h148
-rw-r--r--libs/androidfw/include/androidfw/Asset.h25
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h20
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp18
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp6
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp2
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp4
-rw-r--r--libs/androidfw/tests/Theme_test.cpp2
-rw-r--r--startop/view_compiler/apk_layout_compiler.cc9
94 files changed, 2000 insertions, 1808 deletions
diff --git a/api/current.txt b/api/current.txt
index 4db6b33c9ccc..523d00c7cc43 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12872,14 +12872,7 @@ package android.content.res {
package android.content.res.loader {
public interface AssetsProvider {
- method @Nullable public default java.io.InputStream loadAsset(@NonNull String, int) throws java.io.IOException;
- method @Nullable public default android.os.ParcelFileDescriptor loadAssetParcelFd(@NonNull String) throws java.io.IOException;
- }
-
- public class DirectoryAssetsProvider implements android.content.res.loader.AssetsProvider {
- ctor public DirectoryAssetsProvider(@NonNull java.io.File);
- method @Nullable public java.io.File findFile(@NonNull String);
- method @NonNull public java.io.File getDirectory();
+ method @Nullable public default android.content.res.AssetFileDescriptor loadAssetFd(@NonNull String, int);
}
public class ResourcesLoader {
@@ -12894,14 +12887,11 @@ package android.content.res.loader {
public class ResourcesProvider implements java.lang.AutoCloseable java.io.Closeable {
method public void close();
method @NonNull public static android.content.res.loader.ResourcesProvider empty(@NonNull android.content.res.loader.AssetsProvider);
- method @Nullable public android.content.res.loader.AssetsProvider getAssetsProvider();
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.ParcelFileDescriptor, @Nullable android.content.res.loader.AssetsProvider) 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 loadFromApk(@NonNull android.os.SharedMemory, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromDirectory(@NonNull String, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
method @NonNull public static android.content.res.loader.ResourcesProvider loadFromSplit(@NonNull android.content.Context, @NonNull String) throws java.io.IOException;
method @NonNull public static android.content.res.loader.ResourcesProvider loadFromTable(@NonNull android.os.ParcelFileDescriptor, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
- method @NonNull public static android.content.res.loader.ResourcesProvider loadFromTable(@NonNull android.os.SharedMemory, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 9f5dee98acb7..495bb6b43718 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -346,10 +346,9 @@ public class ResourcesManager {
// We must load this from disk.
if (overlay) {
- apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path),
- false /*system*/);
+ apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/);
} else {
- apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
+ apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
if (mLoadedApkAssets != null) {
@@ -1256,7 +1255,8 @@ public class ResourcesManager {
* instance uses.
*/
@Override
- public void onLoadersChanged(Resources resources, List<ResourcesLoader> newLoader) {
+ public void onLoadersChanged(@NonNull Resources resources,
+ @NonNull List<ResourcesLoader> newLoader) {
synchronized (ResourcesManager.this) {
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
@@ -1284,7 +1284,7 @@ public class ResourcesManager {
* {@code loader} to apply any changes of the set of {@link ApkAssets}.
**/
@Override
- public void onLoaderUpdated(ResourcesLoader loader) {
+ public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
synchronized (ResourcesManager.this) {
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
new ArrayMap<>();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 18f13431c09a..20ddba41bda1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1443,7 +1443,7 @@ public class PackageParser {
try {
try {
apkAssets = fd != null
- ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4c6da03b4db3..27399e4b39bc 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -220,7 +220,7 @@ public class ApkLiteParseUtils {
try {
try {
apkAssets = fd != null
- ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
throw new PackageParser.PackageParserException(
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 8db278579cc1..bc418061e1d1 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -15,17 +15,20 @@
*/
package android.content.res;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.om.OverlayableInfo;
+import android.content.res.loader.AssetsProvider;
import android.content.res.loader.ResourcesProvider;
-import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -39,14 +42,79 @@ import java.util.Objects;
* @hide
*/
public final class ApkAssets {
- @GuardedBy("this") private final long mNativePtr;
+
+ /**
+ * The apk assets contains framework resource values specified by the system.
+ * This allows some functions to filter out this package when computing what
+ * configurations/resources are available.
+ */
+ public static final int PROPERTY_SYSTEM = 1 << 0;
+
+ /**
+ * The apk assets is a shared library or was loaded as a shared library by force.
+ * The package ids of dynamic apk assets are assigned at runtime instead of compile time.
+ */
+ public static final int PROPERTY_DYNAMIC = 1 << 1;
+
+ /**
+ * The apk assets has been loaded dynamically using a {@link ResourcesProvider}.
+ * Loader apk assets overlay resources like RROs except they are not backed by an idmap.
+ */
+ public static final int PROPERTY_LOADER = 1 << 2;
+
+ /**
+ * The apk assets is a RRO.
+ * An RRO overlays resource values of its target package.
+ */
+ private static final int PROPERTY_OVERLAY = 1 << 3;
+
+ /** Flags that change the behavior of loaded apk assets. */
+ @IntDef(prefix = { "PROPERTY_" }, value = {
+ PROPERTY_SYSTEM,
+ PROPERTY_DYNAMIC,
+ PROPERTY_LOADER,
+ PROPERTY_OVERLAY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PropertyFlags {}
+
+ /** The path used to load the apk assets represents an APK file. */
+ private static final int FORMAT_APK = 0;
+
+ /** The path used to load the apk assets represents an idmap file. */
+ private static final int FORMAT_IDMAP = 1;
+
+ /** The path used to load the apk assets represents an resources.arsc file. */
+ private static final int FORMAT_ARSC = 2;
+
+ /** the path used to load the apk assets represents a directory. */
+ private static final int FORMAT_DIR = 3;
+
+ // Format types that change how the apk assets are loaded.
+ @IntDef(prefix = { "FORMAT_" }, value = {
+ FORMAT_APK,
+ FORMAT_IDMAP,
+ FORMAT_ARSC,
+ FORMAT_DIR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FormatType {}
+
+ @GuardedBy("this")
+ private final long mNativePtr;
@Nullable
- @GuardedBy("this") private final StringBlock mStringBlock;
+ @GuardedBy("this")
+ private final StringBlock mStringBlock;
- @GuardedBy("this") private boolean mOpen = true;
+ @GuardedBy("this")
+ private boolean mOpen = true;
- private final boolean mForLoader;
+ @PropertyFlags
+ private final int mFlags;
+
+ @Nullable
+ private final AssetsProvider mAssets;
/**
* Creates a new ApkAssets instance from the given path on disk.
@@ -56,157 +124,202 @@ 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*/,
- false /*arscOnly*/, false /*forLoader*/);
+ return loadFromPath(path, 0 /* flags */);
}
/**
* Creates a new ApkAssets instance from the given path on disk.
*
* @param path The path to an APK on disk.
- * @param system When true, the APK is loaded as a system APK (framework).
+ * @param flags flags that change the behavior of loaded apk assets
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
- public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
+ public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
throws IOException {
- return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
- false /*arscOnly*/, false /*forLoader*/);
+ return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
}
/**
* Creates a new ApkAssets instance from the given path on disk.
*
* @param path The path to an APK on disk.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
- * loaded as a shared library.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
- public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
- boolean forceSharedLibrary) throws IOException {
- return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
- false /*arscOnly*/, false /*forLoader*/);
+ public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_APK, path, flags, assets);
}
/**
- * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
+ * Creates a new ApkAssets instance from the given file descriptor.
*
* 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.
* @param friendlyName The friendly name used to identify this ApkAssets when logging.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
- * loaded as a shared library.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
- throws IOException {
- return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
- false /*forLoader*/);
+ @NonNull String friendlyName, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
}
/**
- * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
- * is encoded within the IDMAP.
+ * Creates a new ApkAssets instance from the given file descriptor.
*
- * @param idmapPath Path to the IDMAP of an overlay APK.
- * @param system When true, the APK is loaded as a system APK (framework).
+ * 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.
+ * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+ * @param offset The location within the file that the apk starts. This must be 0 if length is
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+ * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+ * if it extends to the end of the file.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
- public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
+ public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets)
throws IOException {
- return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
- false /*arscOnly*/, false /*forLoader*/);
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
}
/**
- * Creates a new ApkAssets instance from the given path on disk for use with a
- * {@link ResourcesProvider}.
+ * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
+ * is encoded within the IDMAP.
*
- * @param path The path to an APK on disk.
+ * @param idmapPath Path to the IDMAP of an overlay APK.
+ * @param flags flags that change the behavior of loaded apk assets
* @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*/);
+ public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
+ @PropertyFlags int flags) throws IOException {
+ return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
}
/**
- * Creates a new ApkAssets instance from the given file descriptor for use with a
- * {@link ResourcesProvider}.
+ * Creates a new ApkAssets instance from the given file descriptor representing a resources.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 APK.
+ * @param fd The FileDescriptor of an open, readable resources.arsc.
+ * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
* @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*/);
+ public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
}
/**
- * Creates a new ApkAssets instance from the given file descriptor representing an ARSC
+ * Creates a new ApkAssets instance from the given file descriptor representing a resources.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.
+ * @param fd The FileDescriptor of an open, readable resources.arsc.
+ * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+ * @param offset The location within the file that the table starts. This must be 0 if length is
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+ * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+ * if it extends to the end of the file.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags, assets);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given directory path. The directory should have the
+ * file structure of an APK.
+ *
+ * @param path The path to a directory on disk.
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
* @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*/);
+ public static @NonNull ApkAssets loadFromDir(@NonNull String path,
+ @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_DIR, path, flags, assets);
}
/**
* 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.
+ *
+ * @param flags flags that change the behavior of loaded apk assets
+ * @param assets The assets provider that overrides the loading of file-based resources
*/
@NonNull
- public static ApkAssets loadEmptyForLoader() {
- return new ApkAssets(true);
+ public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
+ @Nullable AssetsProvider assets) {
+ return new ApkAssets(flags, assets);
}
- private ApkAssets(boolean forLoader) {
- mForLoader = forLoader;
- mNativePtr = nativeLoadEmpty(forLoader);
- mStringBlock = null;
+ private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ Objects.requireNonNull(path, "path");
+ mFlags = flags;
+ mNativePtr = nativeLoad(format, path, flags, assets);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
- private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
- boolean arscOnly, boolean forLoader) throws IOException {
- mForLoader = forLoader;
- Objects.requireNonNull(path, "path");
- mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
- : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
+ private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
+ @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
+ throws IOException {
+ Objects.requireNonNull(fd, "fd");
+ Objects.requireNonNull(friendlyName, "friendlyName");
+ mFlags = flags;
+ mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
- private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
- boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
- mForLoader = forLoader;
+ private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
- : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
+ mFlags = flags;
+ mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
+ }
+
+ private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
+ mFlags = flags;
+ mNativePtr = nativeLoadEmpty(flags, assets);
+ mStringBlock = null;
+ mAssets = assets;
}
@UnsupportedAppUsage
@@ -226,8 +339,17 @@ public final class ApkAssets {
}
}
+ /** Returns whether this apk assets was loaded using a {@link ResourcesProvider}. */
public boolean isForLoader() {
- return mForLoader;
+ return (mFlags & PROPERTY_LOADER) != 0;
+ }
+
+ /**
+ * Returns the assets provider that overrides the loading of assets present in this apk assets.
+ */
+ @Nullable
+ public AssetsProvider getAssetsProvider() {
+ return mAssets;
}
/**
@@ -300,18 +422,16 @@ public final class ApkAssets {
}
}
- 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,
- 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 long nativeLoad(@FormatType int format, @NonNull String path,
+ @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
+ private static native long nativeLoadEmpty(@PropertyFlags int flags,
+ @Nullable AssetsProvider asset);
+ private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
+ @NonNull String friendlyName, @PropertyFlags int flags,
+ @Nullable AssetsProvider asset) throws IOException;
+ private static native long nativeLoadFdOffsets(@FormatType int format,
+ @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
+ @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
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 f295f8c531e9..d2103af1d247 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -27,9 +27,7 @@ import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
-import android.content.res.loader.AssetsProvider;
import android.content.res.loader.ResourcesLoader;
-import android.content.res.loader.ResourcesProvider;
import android.os.ParcelFileDescriptor;
import android.util.ArraySet;
import android.util.Log;
@@ -44,7 +42,6 @@ import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -151,12 +148,9 @@ public final class AssetManager implements AutoCloseable {
final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets();
for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
final ApkAssets apkAssets = currentLoaderApkAssets.get(j);
- if (uniqueLoaderApkAssets.contains(apkAssets)) {
- continue;
+ if (uniqueLoaderApkAssets.add(apkAssets)) {
+ loaderApkAssets.add(0, apkAssets);
}
-
- uniqueLoaderApkAssets.add(apkAssets);
- loaderApkAssets.add(0, apkAssets);
}
}
@@ -242,12 +236,12 @@ public final class AssetManager implements AutoCloseable {
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
- apkAssets.add(ApkAssets.loadFromPath(frameworkPath, true /*system*/));
+ apkAssets.add(ApkAssets.loadFromPath(frameworkPath, ApkAssets.PROPERTY_SYSTEM));
final String[] systemIdmapPaths =
OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote();
for (String idmapPath : systemIdmapPaths) {
- apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM));
}
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
@@ -332,6 +326,42 @@ public final class AssetManager implements AutoCloseable {
}
/**
+ * Changes the {@link ResourcesLoader ResourcesLoaders} used in this AssetManager.
+ * @hide
+ */
+ void setLoaders(@NonNull List<ResourcesLoader> newLoaders) {
+ Objects.requireNonNull(newLoaders, "newLoaders");
+
+ final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ for (int i = 0; i < mApkAssets.length; i++) {
+ // Filter out the previous loader apk assets.
+ if (!mApkAssets[i].isForLoader()) {
+ apkAssets.add(mApkAssets[i]);
+ }
+ }
+
+ if (!newLoaders.isEmpty()) {
+ // Filter so that assets provided by multiple loaders are only included once
+ // in the final assets list. The last appearance of the ApkAssets dictates its load
+ // order.
+ final int loaderStartIndex = apkAssets.size();
+ final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
+ for (int i = newLoaders.size() - 1; i >= 0; i--) {
+ final List<ApkAssets> currentLoaderApkAssets = newLoaders.get(i).getApkAssets();
+ for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
+ final ApkAssets loaderApkAssets = currentLoaderApkAssets.get(j);
+ if (uniqueLoaderApkAssets.add(loaderApkAssets)) {
+ apkAssets.add(loaderStartIndex, loaderApkAssets);
+ }
+ }
+ }
+ }
+
+ mLoaders = newLoaders.toArray(new ResourcesLoader[0]);
+ setApkAssets(apkAssets.toArray(new ApkAssets[0]), true /* invalidate_caches */);
+ }
+
+ /**
* Invalidates the caches in this AssetManager according to the bitmask `diff`.
*
* @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
@@ -443,9 +473,10 @@ public final class AssetManager implements AutoCloseable {
final String idmapPath = "/data/resource-cache/"
+ path.substring(1).replace('/', '@')
+ "@idmap";
- assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
+ assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */);
} else {
- assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
+ assets = ApkAssets.loadFromPath(path,
+ appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
} catch (IOException e) {
return 0;
@@ -827,13 +858,6 @@ public final class AssetManager implements AutoCloseable {
Objects.requireNonNull(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);
@@ -858,13 +882,6 @@ public final class AssetManager implements AutoCloseable {
Objects.requireNonNull(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);
@@ -958,12 +975,6 @@ public final class AssetManager implements AutoCloseable {
Objects.requireNonNull(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);
@@ -1003,12 +1014,6 @@ public final class AssetManager implements AutoCloseable {
Objects.requireNonNull(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) {
@@ -1071,15 +1076,7 @@ public final class AssetManager implements AutoCloseable {
synchronized (this) {
ensureOpenLocked();
- final long xmlBlock;
- AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName);
- if (fileDescriptor != null) {
- xmlBlock = nativeOpenXmlAssetFd(mObject, cookie,
- fileDescriptor.getFileDescriptor());
- } else {
- xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
- }
-
+ final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
if (xmlBlock == 0) {
throw new FileNotFoundException("Asset XML file: " + fileName);
}
@@ -1089,122 +1086,6 @@ public final class AssetManager implements AutoCloseable {
}
}
- private ResourcesProvider findResourcesProvider(int assetCookie) {
- if (mLoaders == null) {
- return null;
- }
-
- int apkAssetsIndex = assetCookie - 1;
- if (apkAssetsIndex >= mApkAssets.length || apkAssetsIndex < 0) {
- return null;
- }
-
- final ApkAssets apkAssets = mApkAssets[apkAssetsIndex];
- if (!apkAssets.isForLoader()) {
- return null;
- }
-
- for (int i = mLoaders.length - 1; i >= 0; i--) {
- final ResourcesLoader loader = mLoaders[i];
- for (int j = 0, n = loader.getProviders().size(); j < n; j++) {
- final ResourcesProvider provider = loader.getProviders().get(j);
- if (apkAssets == provider.getApkAssets()) {
- return provider;
- }
- }
- }
-
- return null;
- }
-
- private InputStream searchLoaders(int cookie, @NonNull String fileName, int accessMode)
- throws IOException {
- if (mLoaders == null) {
- return null;
- }
-
- if (cookie == 0) {
- // A cookie of 0 means no specific ApkAssets, so search everything
- for (int i = mLoaders.length - 1; i >= 0; i--) {
- final ResourcesLoader loader = mLoaders[i];
- final List<ResourcesProvider> providers = loader.getProviders();
- for (int j = providers.size() - 1; j >= 0; j--) {
- final AssetsProvider assetsProvider = providers.get(j).getAssetsProvider();
- if (assetsProvider == null) {
- continue;
- }
-
- try {
- final InputStream inputStream = assetsProvider.loadAsset(
- fileName, accessMode);
- if (inputStream != null) {
- return inputStream;
- }
- } catch (IOException ignored) {
- // When searching, ignore read failures
- }
- }
- }
-
- return null;
- }
-
- final ResourcesProvider provider = findResourcesProvider(cookie);
- if (provider != null && provider.getAssetsProvider() != null) {
- return provider.getAssetsProvider().loadAsset(
- fileName, accessMode);
- }
-
- return null;
- }
-
- private AssetFileDescriptor searchLoadersFd(int cookie, @NonNull String fileName)
- throws IOException {
- if (mLoaders == null) {
- return null;
- }
-
- if (cookie == 0) {
- // A cookie of 0 means no specific ApkAssets, so search everything
- for (int i = mLoaders.length - 1; i >= 0; i--) {
- final ResourcesLoader loader = mLoaders[i];
- final List<ResourcesProvider> providers = loader.getProviders();
- for (int j = providers.size() - 1; j >= 0; j--) {
- final AssetsProvider assetsProvider = providers.get(j).getAssetsProvider();
- if (assetsProvider == null) {
- continue;
- }
-
- try {
- final ParcelFileDescriptor fileDescriptor = assetsProvider
- .loadAssetParcelFd(fileName);
- if (fileDescriptor != null) {
- return new AssetFileDescriptor(fileDescriptor, 0,
- AssetFileDescriptor.UNKNOWN_LENGTH);
- }
- } catch (IOException ignored) {
- // When searching, ignore read failures
- }
- }
- }
-
- return null;
- }
-
- final ResourcesProvider provider = findResourcesProvider(cookie);
- if (provider != null && provider.getAssetsProvider() != null) {
- final ParcelFileDescriptor fileDescriptor = provider.getAssetsProvider()
- .loadAssetParcelFd(fileName);
- if (fileDescriptor != null) {
- return new AssetFileDescriptor(fileDescriptor, 0,
- AssetFileDescriptor.UNKNOWN_LENGTH);
- }
- return null;
- }
-
- return null;
- }
-
void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index cb809da3b867..d6a9f6990abe 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -66,6 +66,7 @@ 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.Preconditions;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -244,7 +245,36 @@ public class Resources {
* @param resources the instance being updated
* @param newLoaders the new set of loaders for the instance
*/
- void onLoadersChanged(Resources resources, List<ResourcesLoader> newLoaders);
+ void onLoadersChanged(@NonNull Resources resources,
+ @NonNull List<ResourcesLoader> newLoaders);
+ }
+
+ /**
+ * Handler that propagates updates of the {@link Resources} instance to the underlying
+ * {@link AssetManager} when the Resources is not registered with a
+ * {@link android.app.ResourcesManager}.
+ * @hide
+ */
+ public class AssetManagerUpdateHandler implements UpdateCallbacks{
+
+ @Override
+ public void onLoadersChanged(@NonNull Resources resources,
+ @NonNull List<ResourcesLoader> newLoaders) {
+ Preconditions.checkArgument(Resources.this == resources);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.clearAllCaches();
+ impl.getAssets().setLoaders(newLoaders);
+ }
+
+ @Override
+ public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
+ final ResourcesImpl impl = mResourcesImpl;
+ final AssetManager assets = impl.getAssets();
+ if (assets.getLoaders().contains(loader)) {
+ impl.clearAllCaches();
+ assets.setLoaders(assets.getLoaders());
+ }
+ }
}
/**
@@ -2367,8 +2397,9 @@ public class Resources {
private void checkCallbacksRegistered() {
if (mCallbacks == null) {
- throw new IllegalArgumentException("Cannot modify resource loaders of Resources"
- + " instances created outside of ResourcesManager");
+ // Fallback to updating the underlying AssetManager if the Resources is not associated
+ // with a ResourcesManager.
+ mCallbacks = new AssetManagerUpdateHandler();
}
}
@@ -2388,6 +2419,9 @@ public class Resources {
* Adds a loader to the list of loaders. If the loader is already present in the list, the list
* will not be modified.
*
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * loader changes.
+ *
* @param loaders the loaders to add
*/
public void addLoaders(@NonNull ResourcesLoader... loaders) {
@@ -2419,6 +2453,9 @@ public class Resources {
* Removes loaders from the list of loaders. If the loader is not present in the list, the list
* will not be modified.
*
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * loader changes.
+ *
* @param loaders the loaders to remove
*/
public void removeLoaders(@NonNull ResourcesLoader... loaders) {
@@ -2448,6 +2485,9 @@ public class Resources {
/**
* Removes all {@link ResourcesLoader ResourcesLoader(s)}.
+ *
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * loader changes.
* @hide
*/
@VisibleForTesting
diff --git a/core/java/android/content/res/loader/AssetsProvider.java b/core/java/android/content/res/loader/AssetsProvider.java
index c315494cf728..0f8f1d1da8d2 100644
--- a/core/java/android/content/res/loader/AssetsProvider.java
+++ b/core/java/android/content/res/loader/AssetsProvider.java
@@ -18,12 +18,10 @@ package android.content.res.loader;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.os.ParcelFileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-
/**
* Provides callbacks that allow for the value of a file-based resources or assets of a
* {@link ResourcesProvider} to be specified or overridden.
@@ -34,6 +32,10 @@ public interface AssetsProvider {
* Callback that allows the value of a file-based resources or asset to be specified or
* overridden.
*
+ * <p>The system will take ownership of the file descriptor returned from this method, so
+ * {@link ParcelFileDescriptor#dup() dup} the file descriptor before returning if the system
+ * should not own it.
+ *
* <p>There are two situations in which this method will be called:
* <ul>
* <li>AssetManager is queried for an InputStream of an asset using APIs like
@@ -52,17 +54,7 @@ public interface AssetsProvider {
* @see AssetManager#open
*/
@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 being loaded
- */
- @Nullable
- default ParcelFileDescriptor loadAssetParcelFd(@NonNull String path) throws IOException {
+ default AssetFileDescriptor loadAssetFd(@NonNull String path, int accessMode) {
return null;
}
}
diff --git a/core/java/android/content/res/loader/DirectoryAssetsProvider.java b/core/java/android/content/res/loader/DirectoryAssetsProvider.java
deleted file mode 100644
index 81c2a4c1b4d6..000000000000
--- a/core/java/android/content/res/loader/DirectoryAssetsProvider.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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 AssetsProvider} that searches a directory for assets.
- * Assumes that resource paths are resolvable child paths of the root directory passed in.
- */
-public class DirectoryAssetsProvider implements AssetsProvider {
-
- @NonNull
- private final File mDirectory;
-
- /**
- * Creates a DirectoryAssetsProvider with given root directory.
- *
- * @param directory the root directory to resolve files from
- */
- public DirectoryAssetsProvider(@NonNull File directory) {
- this.mDirectory = directory;
- }
-
- @Nullable
- @Override
- public InputStream loadAsset(@NonNull String path, int accessMode) throws IOException {
- final File file = findFile(path);
- if (file == null || !file.exists()) {
- return null;
- }
- return new FileInputStream(file);
- }
-
- @Nullable
- @Override
- public ParcelFileDescriptor loadAssetParcelFd(@NonNull String path) throws IOException {
- final File file = findFile(path);
- if (file == null || !file.exists()) {
- return null;
- }
- return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- }
-
- /**
- * Finds the file relative to the root directory.
- *
- * @param path the relative path of the file
- */
- @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/ResourcesLoader.java b/core/java/android/content/res/loader/ResourcesLoader.java
index 58fec603a2d5..c3084003c304 100644
--- a/core/java/android/content/res/loader/ResourcesLoader.java
+++ b/core/java/android/content/res/loader/ResourcesLoader.java
@@ -45,6 +45,11 @@ import java.util.List;
*
* <p>Providers retrieved with {@link #getProviders()} are listed in increasing precedence order. A
* provider will override the resources and assets of providers listed before itself.
+ *
+ * <p>Modifying the list of providers a loader contains or the list of loaders a Resources object
+ * contains can cause lock contention with the UI thread. APIs that modify the lists of loaders or
+ * providers should only be used on the UI thread. Providers can be instantiated on any thread
+ * without causing lock contention.
*/
public class ResourcesLoader {
private final Object mLock = new Object();
@@ -88,6 +93,9 @@ public class ResourcesLoader {
* Appends a provider to the end of the provider list. If the provider is already present in the
* loader list, the list will not be modified.
*
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * provider changes.
+ *
* @param resourcesProvider the provider to add
*/
public void addProvider(@NonNull ResourcesProvider resourcesProvider) {
@@ -102,6 +110,9 @@ public class ResourcesLoader {
* Removes a provider from the provider list. If the provider is not present in the provider
* list, the list will not be modified.
*
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * provider changes.
+ *
* @param resourcesProvider the provider to remove
*/
public void removeProvider(@NonNull ResourcesProvider resourcesProvider) {
@@ -115,6 +126,9 @@ public class ResourcesLoader {
/**
* Sets the list of providers.
*
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * provider changes.
+ *
* @param resourcesProviders the new providers
*/
public void setProviders(@NonNull List<ResourcesProvider> resourcesProviders) {
@@ -124,7 +138,12 @@ public class ResourcesLoader {
}
}
- /** Removes all {@link ResourcesProvider ResourcesProvider(s)}. */
+ /**
+ * Removes all {@link ResourcesProvider ResourcesProvider(s)}.
+ *
+ * <p>This should only be called from the UI thread to avoid lock contention when propagating
+ * provider changes.
+ */
public void clearProviders() {
synchronized (mLock) {
mProviders = null;
@@ -206,7 +225,6 @@ public class ResourcesLoader {
return true;
}
-
/**
* Invokes registered callbacks when the list of {@link ResourcesProvider} instances this loader
* uses changes.
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
index 419ec7882f3d..0a698d18682b 100644
--- a/core/java/android/content/res/loader/ResourcesProvider.java
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -21,19 +21,21 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.ApkAssets;
+import android.content.res.AssetFileDescriptor;
import android.os.ParcelFileDescriptor;
-import android.os.SharedMemory;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
/**
* Provides methods to load resources data from APKs ({@code .apk}) and resources tables
- * {@code .arsc} for use with {@link ResourcesLoader ResourcesLoader(s)}.
+ * (eg. {@code resources.arsc}) for use with {@link ResourcesLoader ResourcesLoader(s)}.
*/
public class ResourcesProvider implements AutoCloseable, Closeable {
private static final String TAG = "ResourcesProvider";
@@ -48,105 +50,143 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
@GuardedBy("mLock")
private final ApkAssets mApkAssets;
- private final AssetsProvider mAssetsProvider;
-
/**
- * Creates an empty ResourcesProvider with no resource data. This is useful for loading assets
- * that are not associated with resource identifiers.
+ * Creates an empty ResourcesProvider with no resource data. This is useful for loading
+ * file-based assets not associated with resource identifiers.
*
- * @param assetsProvider the assets provider that overrides the loading of file-based resources
+ * @param assetsProvider the assets provider that implements the loading of file-based resources
*/
@NonNull
public static ResourcesProvider empty(@NonNull AssetsProvider assetsProvider) {
- return new ResourcesProvider(ApkAssets.loadEmptyForLoader(), assetsProvider);
+ return new ResourcesProvider(ApkAssets.loadEmptyForLoader(ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
* Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
*
- * The file descriptor is duplicated and the original may be closed by the application at any
+ * <p>The file descriptor is duplicated and the original may be closed by the application at any
* time without affecting the ResourcesProvider.
*
* @param fileDescriptor the file descriptor of the APK to load
+ *
+ * @see ParcelFileDescriptor#open(File, int)
+ * @see android.system.Os#memfd_create(String, int)
*/
@NonNull
public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor)
throws IOException {
- return loadFromApk(fileDescriptor, null);
+ return loadFromApk(fileDescriptor, null /* assetsProvider */);
}
/**
* Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
*
- * The file descriptor is duplicated and the original may be closed by the application at any
+ * <p>The file descriptor is duplicated and the original may be closed by the application at any
* time without affecting the ResourcesProvider.
*
+ * <p>The assets provider can override the loading of files within the APK and can provide
+ * entirely new files that do not exist in the APK.
+ *
* @param fileDescriptor the file descriptor of the APK to load
* @param assetsProvider the assets provider that overrides the loading of file-based resources
+ *
+ * @see ParcelFileDescriptor#open(File, int)
+ * @see android.system.Os#memfd_create(String, int)
*/
@NonNull
public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor,
@Nullable AssetsProvider assetsProvider)
throws IOException {
- return new ResourcesProvider(
- ApkAssets.loadApkForLoader(fileDescriptor.getFileDescriptor()), assetsProvider);
+ return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
+ fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
}
/**
- * Creates a ResourcesProvider from an {@code .apk} file representation in memory.
+ * Creates a ResourcesProvider from an APK ({@code .apk}) file descriptor.
*
- * @param sharedMemory the shared memory containing the data of the APK to load
- */
- @NonNull
- public static ResourcesProvider loadFromApk(@NonNull SharedMemory sharedMemory)
- throws IOException {
- return loadFromApk(sharedMemory, null);
- }
-
- /**
- * Creates a ResourcesProvider from an {@code .apk} file representation in memory.
+ * <p>The file descriptor is duplicated and the original may be closed by the application at any
+ * time without affecting the ResourcesProvider.
*
- * @param sharedMemory the shared memory containing the data of the APK to load
- * @param assetsProvider the assets provider that implements the loading of file-based resources
+ * <p>The assets provider can override the loading of files within the APK and can provide
+ * entirely new files that do not exist in the APK.
+ *
+ * @param fileDescriptor the file descriptor of the APK to load
+ * @param offset The location within the file that the apk starts. This must be 0 if length is
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+ * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+ * if it extends to the end of the file.
+ * @param assetsProvider the assets provider that overrides the loading of file-based resources
+ *
+ * @see ParcelFileDescriptor#open(File, int)
+ * @see android.system.Os#memfd_create(String, int)
+ * @hide
*/
+ @VisibleForTesting
@NonNull
- public static ResourcesProvider loadFromApk(@NonNull SharedMemory sharedMemory,
- @Nullable AssetsProvider assetsProvider)
+ public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor,
+ long offset, long length, @Nullable AssetsProvider assetsProvider)
throws IOException {
- return new ResourcesProvider(
- ApkAssets.loadApkForLoader(sharedMemory.getFileDescriptor()), assetsProvider);
+ return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
+ fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
* Creates a ResourcesProvider from a resources table ({@code .arsc}) file descriptor.
*
- * The file descriptor is duplicated and the original may be closed by the application at any
+ * <p>The file descriptor is duplicated and the original may be closed by the application at any
* time without affecting the ResourcesProvider.
*
+ * <p>The resources table format is not an archive format and therefore cannot asset files
+ * within itself. The assets provider can instead provide files that are potentially referenced
+ * by path in the resources table.
+ *
* @param fileDescriptor the file descriptor of the resources table to load
* @param assetsProvider the assets provider that implements the loading of file-based resources
+ *
+ * @see ParcelFileDescriptor#open(File, int)
+ * @see android.system.Os#memfd_create(String, int)
*/
@NonNull
public static ResourcesProvider loadFromTable(@NonNull ParcelFileDescriptor fileDescriptor,
@Nullable AssetsProvider assetsProvider)
throws IOException {
return new ResourcesProvider(
- ApkAssets.loadArscForLoader(fileDescriptor.getFileDescriptor()), assetsProvider);
+ ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
+ fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
}
/**
- * Creates a ResourcesProvider from a resources table ({@code .arsc}) file representation in
- * memory.
+ * Creates a ResourcesProvider from a resources table ({@code .arsc}) file descriptor.
*
- * @param sharedMemory the shared memory containing the data of the resources table to load
+ * The file descriptor is duplicated and the original may be closed by the application at any
+ * time without affecting the ResourcesProvider.
+ *
+ * <p>The resources table format is not an archive format and therefore cannot asset files
+ * within itself. The assets provider can instead provide files that are potentially referenced
+ * by path in the resources table.
+ *
+ * @param fileDescriptor the file descriptor of the resources table to load
+ * @param offset The location within the file that the table starts. This must be 0 if length is
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
+ * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
+ * if it extends to the end of the file.
* @param assetsProvider the assets provider that overrides the loading of file-based resources
+ *
+ * @see ParcelFileDescriptor#open(File, int)
+ * @see android.system.Os#memfd_create(String, int)
+ * @hide
*/
+ @VisibleForTesting
@NonNull
- public static ResourcesProvider loadFromTable(@NonNull SharedMemory sharedMemory,
- @Nullable AssetsProvider assetsProvider)
+ public static ResourcesProvider loadFromTable(@NonNull ParcelFileDescriptor fileDescriptor,
+ long offset, long length, @Nullable AssetsProvider assetsProvider)
throws IOException {
return new ResourcesProvider(
- ApkAssets.loadArscForLoader(sharedMemory.getFileDescriptor()), assetsProvider);
+ ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
+ fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
@@ -166,18 +206,28 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
}
String splitPath = appInfo.getSplitCodePaths()[splitIndex];
- return new ResourcesProvider(ApkAssets.loadApkForLoader(splitPath), null);
+ return new ResourcesProvider(ApkAssets.loadFromPath(splitPath, ApkAssets.PROPERTY_LOADER,
+ null /* assetsProvider */));
}
- private ResourcesProvider(@NonNull ApkAssets apkAssets,
- @Nullable AssetsProvider assetsProvider) {
- this.mApkAssets = apkAssets;
- this.mAssetsProvider = assetsProvider;
+ /**
+ * Creates a ResourcesProvider from a directory path.
+ *
+ * File-based resources will be resolved within the directory as if the directory is an APK.
+ *
+ * @param path the path of the directory to treat as an APK
+ * @param assetsProvider the assets provider that overrides the loading of file-based resources
+ */
+ @NonNull
+ public static ResourcesProvider loadFromDirectory(@NonNull String path,
+ @Nullable AssetsProvider assetsProvider) throws IOException {
+ return new ResourcesProvider(ApkAssets.loadFromDir(path, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
- @Nullable
- public AssetsProvider getAssetsProvider() {
- return mAssetsProvider;
+
+ private ResourcesProvider(@NonNull ApkAssets apkAssets) {
+ this.mApkAssets = apkAssets;
}
/** @hide */
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 491d4a61cee4..fbdd4060d7f2 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -37,8 +37,128 @@ static struct overlayableinfo_offsets_t {
jmethodID constructor;
} gOverlayableInfoOffsets;
-static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
- jboolean force_shared_lib, jboolean overlay, jboolean for_loader) {
+static struct assetfiledescriptor_offsets_t {
+ jfieldID mFd;
+ jfieldID mStartOffset;
+ jfieldID mLength;
+} gAssetFileDescriptorOffsets;
+
+static struct assetsprovider_offsets_t {
+ jclass classObject;
+ jmethodID loadAssetFd;
+} gAssetsProviderOffsets;
+
+static struct {
+ jmethodID detachFd;
+} gParcelFileDescriptorOffsets;
+
+// Keep in sync with f/b/android/content/res/ApkAssets.java
+using format_type_t = jint;
+enum : format_type_t {
+ // The path used to load the apk assets represents an APK file.
+ FORMAT_APK = 0,
+
+ // The path used to load the apk assets represents an idmap file.
+ FORMAT_IDMAP = 1,
+
+ // The path used to load the apk assets represents an resources.arsc file.
+ FORMAT_ARSC = 2,
+
+ // The path used to load the apk assets represents the a directory.
+ FORMAT_DIRECTORY = 3,
+};
+
+class LoaderAssetsProvider : public AssetsProvider {
+ public:
+ static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
+ return (!assets_provider) ? nullptr
+ : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
+ env->NewGlobalRef(assets_provider)));
+ }
+
+ ~LoaderAssetsProvider() override {
+ const auto env = AndroidRuntime::getJNIEnv();
+ CHECK(env != nullptr) << "Current thread not attached to a Java VM."
+ << " Failed to close LoaderAssetsProvider.";
+ env->DeleteGlobalRef(assets_provider_);
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const override {
+ const auto env = AndroidRuntime::getJNIEnv();
+ CHECK(env != nullptr) << "Current thread not attached to a Java VM."
+ << " ResourcesProvider assets cannot be retrieved on current thread.";
+
+ jstring java_string = env->NewStringUTF(path.c_str());
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return nullptr;
+ }
+
+ // Check if the AssetsProvider provides a value for the path.
+ jobject asset_fd = env->CallObjectMethod(assets_provider_,
+ gAssetsProviderOffsets.loadAssetFd,
+ java_string, static_cast<jint>(mode));
+ env->DeleteLocalRef(java_string);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return nullptr;
+ }
+
+ if (!asset_fd) {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+ }
+
+ const jlong mOffset = env->GetLongField(asset_fd, gAssetFileDescriptorOffsets.mStartOffset);
+ const jlong mLength = env->GetLongField(asset_fd, gAssetFileDescriptorOffsets.mLength);
+ jobject mFd = env->GetObjectField(asset_fd, gAssetFileDescriptorOffsets.mFd);
+ env->DeleteLocalRef(asset_fd);
+
+ if (!mFd) {
+ jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return nullptr;
+ }
+
+ // Gain ownership of the file descriptor.
+ const jint fd = env->CallIntMethod(mFd, gParcelFileDescriptorOffsets.detachFd);
+ env->DeleteLocalRef(mFd);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return nullptr;
+ }
+
+ if (file_exists) {
+ *file_exists = true;
+ }
+
+ return ApkAssets::CreateAssetFromFd(base::unique_fd(fd),
+ nullptr /* path */,
+ static_cast<off64_t>(mOffset),
+ static_cast<off64_t>(mLength));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
+
+ explicit LoaderAssetsProvider(jobject assets_provider)
+ : assets_provider_(assets_provider) { }
+
+ // The global reference to the AssetsProvider
+ jobject assets_provider_;
+};
+
+static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
+ jstring java_path, const jint property_flags, jobject assets_provider) {
ScopedUtfChars path(env, java_path);
if (path.c_str() == nullptr) {
return 0;
@@ -46,26 +166,38 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
+ auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
- if (overlay) {
- apk_assets = ApkAssets::LoadOverlay(path.c_str(), system);
- } else if (force_shared_lib) {
- apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
- } else {
- apk_assets = ApkAssets::Load(path.c_str(), system, for_loader);
+ switch (format) {
+ case FORMAT_APK:
+ apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets));
+ break;
+ case FORMAT_IDMAP:
+ apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
+ break;
+ case FORMAT_ARSC:
+ apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets));
+ break;
+ case FORMAT_DIRECTORY:
+ apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags, std::move(loader_assets));
+ break;
+ default:
+ const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
+ jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
+ return 0;
}
if (apk_assets == nullptr) {
- std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
+ const 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 NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
- jstring friendly_name, jboolean system, jboolean force_shared_lib,
- jboolean for_loader) {
+static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
+ jobject file_descriptor, jstring friendly_name,
+ const jint property_flags, jobject assets_provider) {
ScopedUtfChars friendly_name_utf8(env, friendly_name);
if (friendly_name_utf8.c_str() == nullptr) {
return 0;
@@ -85,49 +217,56 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri
return 0;
}
- auto dup_fd_id = dup_fd.get();
- std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
- friendly_name_utf8.c_str(),
- system, force_shared_lib,
- for_loader);
+ auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
+ std::unique_ptr<const ApkAssets> apk_assets;
+ switch (format) {
+ case FORMAT_APK:
+ apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
+ property_flags, std::move(loader_assets));
+ break;
+ case FORMAT_ARSC:
+ apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
+ property_flags, std::move(loader_assets));
+ break;
+ default:
+ const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
+ jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
+ return 0;
+ }
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_id);
+ friendly_name_utf8.c_str(), fd);
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
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) {
+static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
+ jobject file_descriptor, jstring friendly_name,
+ const jlong offset, const jlong length,
+ const jint property_flags, jobject assets_provider) {
+ ScopedUtfChars friendly_name_utf8(env, friendly_name);
+ if (friendly_name_utf8.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);
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
- 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());
+ if (offset < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "offset cannot be negative");
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) {
+ if (length < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "length cannot be negative");
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;
@@ -139,18 +278,39 @@ static jlong NativeLoadArscFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_de
return 0;
}
- std::unique_ptr<const ApkAssets> apk_assets =
- ApkAssets::LoadArsc(std::move(dup_fd), friendly_name_utf8.c_str(), for_loader);
+ auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
+ std::unique_ptr<const ApkAssets> apk_assets;
+ switch (format) {
+ case FORMAT_APK:
+ apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
+ property_flags, std::move(loader_assets),
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(length));
+ break;
+ case FORMAT_ARSC:
+ apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
+ property_flags, std::move(loader_assets),
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(length));
+ break;
+ default:
+ const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
+ jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
+ return 0;
+ }
+
if (apk_assets == nullptr) {
- std::string error_msg = base::StringPrintf("Failed to load asset path from fd %d", fd);
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
+ friendly_name_utf8.c_str(), 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);
+static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
+ auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
+ auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets));
return reinterpret_cast<jlong>(apk_assets.release());
}
@@ -180,8 +340,8 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil
}
const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(),
- Asset::AccessMode::ACCESS_RANDOM);
+ std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
+ path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
if (asset == nullptr) {
jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
return 0;
@@ -252,13 +412,15 @@ static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong pt
// JNI registration.
static const JNINativeMethod gApkAssetsMethods[] = {
- {"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},
+ {"nativeLoad", "(ILjava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
+ (void*)NativeLoad},
+ {"nativeLoadEmpty", "(ILandroid/content/res/loader/AssetsProvider;)J", (void*)NativeLoadEmpty},
+ {"nativeLoadFd",
+ "(ILjava/io/FileDescriptor;Ljava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
+ (void*)NativeLoadFromFd},
+ {"nativeLoadFdOffsets",
+ "(ILjava/io/FileDescriptor;Ljava/lang/String;JJILandroid/content/res/loader/AssetsProvider;)J",
+ (void*)NativeLoadFromFdOffset},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
@@ -275,6 +437,21 @@ int register_android_content_res_ApkAssets(JNIEnv* env) {
gOverlayableInfoOffsets.constructor = GetMethodIDOrDie(env, gOverlayableInfoOffsets.classObject,
"<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+ jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
+ gAssetFileDescriptorOffsets.mFd =
+ GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
+ gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
+ gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+
+ jclass assetsProvider = FindClassOrDie(env, "android/content/res/loader/AssetsProvider");
+ gAssetsProviderOffsets.classObject = MakeGlobalRefOrDie(env, assetsProvider);
+ gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
+ env, gAssetsProviderOffsets.classObject, "loadAssetFd",
+ "(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
+
+ jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
+ gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
+
return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
arraysize(gApkAssetsMethods));
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 289453042099..d1d3cf25a4b5 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -74,12 +74,6 @@ static struct typedvalue_offsets_t {
jfieldID mDensity;
} gTypedValueOffsets;
-static struct assetfiledescriptor_offsets_t {
- jfieldID mFd;
- jfieldID mStartOffset;
- jfieldID mLength;
-} gAssetFileDescriptorOffsets;
-
// This is also used by asset_manager.cpp.
assetmanager_offsets_t gAssetManagerOffsets;
@@ -1595,12 +1589,6 @@ int register_android_content_AssetManager(JNIEnv* env) {
GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
- jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
- gAssetFileDescriptorOffsets.mFd =
- GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
- gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
- gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
-
jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
diff --git a/core/tests/ResourceLoaderTests/Android.bp b/core/tests/ResourceLoaderTests/Android.bp
index fec4628106a5..2b14bca1f7c1 100644
--- a/core/tests/ResourceLoaderTests/Android.bp
+++ b/core/tests/ResourceLoaderTests/Android.bp
@@ -21,44 +21,44 @@ android_test {
],
libs: [
"android.test.runner",
- "android.test.base",
+ "android.test.base"
],
static_libs: [
+ "FrameworksResourceLoaderTests_Providers",
"androidx.test.espresso.core",
"androidx.test.ext.junit",
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth-prebuilt"
],
- resource_zips: [ ":FrameworksResourceLoaderTestsAssets" ],
+ resource_dirs: ["res", "resources/provider_stable/res"],
platform_apis: true,
test_suites: ["device-tests"],
- aaptflags: [
- "--no-compress",
- ],
+ aaptflags: ["-0 .txt"],
data: [
- ":FrameworksResourceLoaderTestsSplitOne",
- ":FrameworksResourceLoaderTestsSplitTwo",
- ":FrameworksResourceLoaderTestsSplitThree",
- ":FrameworksResourceLoaderTestsSplitFour",
- ],
- java_resources: [ "NonAsset.txt" ]
-}
-
-filegroup {
- name: "FrameworksResourceLoaderTestsResources",
- srcs: ["resources"],
+ ":FrameworksResourceLoaderTests_ProviderOne_Split",
+ ":FrameworksResourceLoaderTests_ProviderTwo_Split",
+ ":FrameworksResourceLoaderTests_ProviderThree_Split",
+ ":FrameworksResourceLoaderTests_ProviderFour_Split"
+ ]
}
-genrule {
- name: "FrameworksResourceLoaderTestsAssets",
- srcs: [
- ":framework-res",
- ":FrameworksResourceLoaderTestsResources",
+java_genrule {
+ name: "FrameworksResourceLoaderTests_Providers",
+ tools: ["soong_zip"],
+ srcs : [
+ ":FrameworksResourceLoaderTests_ProviderOne",
+ ":FrameworksResourceLoaderTests_ProviderOne_ARSC",
+ ":FrameworksResourceLoaderTests_ProviderTwo",
+ ":FrameworksResourceLoaderTests_ProviderTwo_ARSC",
+ ":FrameworksResourceLoaderTests_ProviderThree",
+ ":FrameworksResourceLoaderTests_ProviderThree_ARSC",
+ ":FrameworksResourceLoaderTests_ProviderFour",
+ ":FrameworksResourceLoaderTests_ProviderFour_ARSC"
],
- 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" ]
-}
+ out: ["FrameworksResourceLoaderTests_Providers.jar"],
+ cmd: "mkdir -p $(genDir)/assets/ && cp $(in) $(genDir)/assets/ && " +
+ "$(location soong_zip) -o $(out) " +
+ "-L 0 -C $(genDir) -D $(genDir)/assets/"
+} \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/AndroidTest.xml b/core/tests/ResourceLoaderTests/AndroidTest.xml
index d732132bef02..800e7a7124ac 100644
--- a/core/tests/ResourceLoaderTests/AndroidTest.xml
+++ b/core/tests/ResourceLoaderTests/AndroidTest.xml
@@ -22,7 +22,7 @@
<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,FrameworksResourceLoaderTestsSplitThree.apk,FrameworksResourceLoaderTestsSplitFour.apk" />
+ value="FrameworksResourceLoaderTests.apk,FrameworksResourceLoaderTests_ProviderOne_Split.apk,FrameworksResourceLoaderTests_ProviderTwo_Split.apk,FrameworksResourceLoaderTests_ProviderThree_Split.apk,FrameworksResourceLoaderTests_ProviderFour_Split.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/core/tests/ResourceLoaderTests/NonAsset.txt b/core/tests/ResourceLoaderTests/NonAsset.txt
deleted file mode 100644
index 5c0b2cc98d64..000000000000
--- a/core/tests/ResourceLoaderTests/NonAsset.txt
+++ /dev/null
@@ -1 +0,0 @@
-Outside assets directory
diff --git a/core/tests/ResourceLoaderTests/assets/Asset.txt b/core/tests/ResourceLoaderTests/assets/Asset.txt
deleted file mode 100644
index 03f9a0fd146a..000000000000
--- a/core/tests/ResourceLoaderTests/assets/Asset.txt
+++ /dev/null
@@ -1 +0,0 @@
-In assets directory
diff --git a/core/tests/ResourceLoaderTests/assets/asset.txt b/core/tests/ResourceLoaderTests/assets/asset.txt
new file mode 100644
index 000000000000..271704bdc1b5
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/assets/asset.txt
@@ -0,0 +1 @@
+In assets directory \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/assets/base_asset.txt b/core/tests/ResourceLoaderTests/assets/base_asset.txt
new file mode 100644
index 000000000000..8e62cc346238
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/assets/base_asset.txt
@@ -0,0 +1 @@
+Base \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png b/core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_png.png
index 8102d1539d53..8102d1539d53 100644
--- a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_png.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml b/core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_xml.xml
index d1211c50a203..d1211c50a203 100644
--- a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/drawable_xml.xml
diff --git a/core/tests/ResourceLoaderTests/res/values/strings.xml b/core/tests/ResourceLoaderTests/res/values/strings.xml
deleted file mode 100644
index 28b8f73d45a6..000000000000
--- a/core/tests/ResourceLoaderTests/res/values/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<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/res/values/non_asset_bitmap_id.xml b/core/tests/ResourceLoaderTests/res/values/values.xml
index 38b152beb76f..ad785322fcc9 100644
--- a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml
+++ b/core/tests/ResourceLoaderTests/res/values/values.xml
@@ -16,5 +16,6 @@
-->
<resources>
- <public type="drawable" name="non_asset_bitmap" id="0x7f010000" />
+ <dimen name="test">0dp</dimen>
+ <string name="test">Not overlaid</string>
</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/Android.bp b/core/tests/ResourceLoaderTests/resources/Android.bp
new file mode 100644
index 000000000000..18ef64b70927
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/Android.bp
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderOne",
+ manifest: "AndroidManifestApp.xml",
+ asset_dirs: ["provider1/assets"],
+ resource_dirs: ["provider1/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderTwo",
+ manifest: "AndroidManifestApp.xml",
+ asset_dirs: ["provider2/assets"],
+ resource_dirs: ["provider2/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderThree",
+ manifest: "AndroidManifestApp.xml",
+ asset_dirs: ["provider3/assets"],
+ resource_dirs: ["provider3/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderFour",
+ manifest: "AndroidManifestApp.xml",
+ asset_dirs: ["provider4/assets"],
+ resource_dirs: ["provider4/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+// Resources.arsc(s)
+
+genrule {
+ name: "FrameworksResourceLoaderTests_ProviderOne_ARSC",
+ srcs: [":FrameworksResourceLoaderTests_ProviderOne"],
+ cmd: "unzip $(in) resources.arsc -d $(genDir) && "
+ + " mv $(genDir)/resources.arsc $(genDir)/FrameworksResourceLoaderTests_ProviderOne.arsc",
+ out: ["FrameworksResourceLoaderTests_ProviderOne.arsc"]
+}
+
+genrule {
+ name: "FrameworksResourceLoaderTests_ProviderTwo_ARSC",
+ srcs: [":FrameworksResourceLoaderTests_ProviderTwo"],
+ cmd: "unzip $(in) resources.arsc -d $(genDir) && "
+ + " mv $(genDir)/resources.arsc $(genDir)/FrameworksResourceLoaderTests_ProviderTwo.arsc",
+ out: ["FrameworksResourceLoaderTests_ProviderTwo.arsc"]
+}
+
+genrule {
+ name: "FrameworksResourceLoaderTests_ProviderThree_ARSC",
+ srcs: [":FrameworksResourceLoaderTests_ProviderThree"],
+ cmd: "unzip $(in) resources.arsc -d $(genDir) && "
+ + " mv $(genDir)/resources.arsc $(genDir)/FrameworksResourceLoaderTests_ProviderThree.arsc",
+ out: ["FrameworksResourceLoaderTests_ProviderThree.arsc"]
+}
+
+genrule {
+ name: "FrameworksResourceLoaderTests_ProviderFour_ARSC",
+ srcs: [":FrameworksResourceLoaderTests_ProviderFour"],
+ cmd: "unzip $(in) resources.arsc -d $(genDir) && "
+ + " mv $(genDir)/resources.arsc $(genDir)/FrameworksResourceLoaderTests_ProviderFour.arsc",
+ out: ["FrameworksResourceLoaderTests_ProviderFour.arsc"]
+}
+
+// Split APKs
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderOne_Split",
+ manifest: "AndroidManifestSplit1.xml",
+ asset_dirs: ["provider1/assets"],
+ resource_dirs: ["provider1/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderTwo_Split",
+ manifest: "AndroidManifestSplit2.xml",
+ asset_dirs: ["provider2/assets"],
+ resource_dirs: ["provider2/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderThree_Split",
+ manifest: "AndroidManifestSplit3.xml",
+ asset_dirs: ["provider3/assets"],
+ resource_dirs: ["provider3/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+}
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTests_ProviderFour_Split",
+ manifest: "AndroidManifestSplit4.xml",
+ asset_dirs: ["provider4/assets"],
+ resource_dirs: ["provider4/res", "provider_stable/res"],
+ aaptflags: ["-0 .txt"]
+} \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
index 5dd8a966e2b7..c8a3590aaa62 100644
--- a/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,12 +15,9 @@
~ limitations under the License.
-->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.res.loader.test"
- >
+<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
index 5a92ae9e662b..d5fa83f59546 100644
--- a/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,12 +16,9 @@
-->
<!-- Mocks the framework package name so that AAPT2 assigns the correct package -->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="android"
- >
+<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/splits/SplitOne/AndroidManifest.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit1.xml
index b14bd8600f31..5cd4227286a2 100644
--- a/core/tests/ResourceLoaderTests/splits/SplitOne/AndroidManifest.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit1.xml
@@ -18,7 +18,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="android.content.res.loader.test"
- split="split_one"
+ split="FrameworksResourceLoaderTests_ProviderOne_Split"
+ android:isFeatureSplit="true"
>
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
diff --git a/core/tests/ResourceLoaderTests/splits/SplitThree/AndroidManifest.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit2.xml
index ae1579b178f3..b5180e66b3a1 100644
--- a/core/tests/ResourceLoaderTests/splits/SplitThree/AndroidManifest.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit2.xml
@@ -18,7 +18,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="android.content.res.loader.test"
- split="split_three"
+ split="FrameworksResourceLoaderTests_ProviderTwo_Split"
+ android:isFeatureSplit="true"
>
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
diff --git a/core/tests/ResourceLoaderTests/splits/SplitTwo/AndroidManifest.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit3.xml
index aad8c27a1a3b..8ddb89280d60 100644
--- a/core/tests/ResourceLoaderTests/splits/SplitTwo/AndroidManifest.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit3.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -18,7 +18,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="android.content.res.loader.test"
- split="split_two"
+ split="FrameworksResourceLoaderTests_ProviderThree_Split"
+ android:isFeatureSplit="true"
>
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
diff --git a/core/tests/ResourceLoaderTests/splits/SplitFour/AndroidManifest.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit4.xml
index 24a0a2a64afb..b6bf552c9892 100644
--- a/core/tests/ResourceLoaderTests/splits/SplitFour/AndroidManifest.xml
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestSplit4.xml
@@ -18,7 +18,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="android.content.res.loader.test"
- split="split_four"
+ split="FrameworksResourceLoaderTests_ProviderFour_Split"
+ android:isFeatureSplit="true"
>
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
diff --git a/core/tests/ResourceLoaderTests/resources/compileAndLink.sh b/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
deleted file mode 100755
index 8e05aefccd39..000000000000
--- a/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/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 stringThree BOTH AndroidManifestFramework.xml res/values/string_three.xml
-compileAndLink stringFour BOTH AndroidManifestFramework.xml res/values/string_four.xml
-
-compileAndLink dimenOne BOTH AndroidManifestFramework.xml res/values/dimen_one.xml
-compileAndLink dimenTwo BOTH AndroidManifestFramework.xml res/values/dimen_two.xml
-compileAndLink dimenThree BOTH AndroidManifestFramework.xml res/values/dimen_three.xml
-compileAndLink dimenFour BOTH AndroidManifestFramework.xml res/values/dimen_four.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
-
-cp -f "$inDir"/res/layout/layout_three.xml "$genDir"/temp/res/layout/layout.xml
-compileAndLink layoutThree ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
-cp -f "$genDir"/out/layoutThree/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutThree.xml
-
-cp -f "$inDir"/res/layout/layout_four.xml "$genDir"/temp/res/layout/layout.xml
-compileAndLink layoutFour ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
-cp -f "$genDir"/out/layoutFour/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutFour.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"/nonAssetDrawableThree.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
-compileAndLink nonAssetDrawableThree ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
-cp -f "$genDir"/out/nonAssetDrawableThree/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableThree.xml
-
-cp -f "$inDirDrawableNoDpi"/nonAssetDrawableFour.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
-compileAndLink nonAssetDrawableFour ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
-cp -f "$genDir"/out/nonAssetDrawableFour/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableFour.xml
-
-cp -f "$inDirDrawableNoDpi"/nonAssetBitmapRed.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
-compileAndLink nonAssetBitmapRed BOTH AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
-cp -f "$genDir"/out/nonAssetBitmapRed/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapRed.png
-
-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
-
-cp -f "$inDirDrawableNoDpi"/nonAssetBitmapWhite.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
-compileAndLink nonAssetBitmapWhite ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
-cp -f "$genDir"/out/nonAssetBitmapWhite/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapWhite.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/framework/res/drawable-mdpi/ic_delete.png
index f3e53d7596c1..f3e53d7596c1 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png
+++ b/core/tests/ResourceLoaderTests/resources/framework/res/drawable-mdpi/ic_delete.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml b/core/tests/ResourceLoaderTests/resources/framework/res/layout/activity_list_item.xml
index d59059b453d6..d59059b453d6 100644
--- a/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml
+++ b/core/tests/ResourceLoaderTests/resources/framework/res/layout/activity_list_item.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_four.xml b/core/tests/ResourceLoaderTests/resources/framework/res/values/public.xml
index 8789bcdb066c..2e501826e00a 100644
--- a/core/tests/ResourceLoaderTests/resources/res/values/string_four.xml
+++ b/core/tests/ResourceLoaderTests/resources/framework/res/values/public.xml
@@ -16,6 +16,7 @@
-->
<resources>
+ <public type="drawable" name="ic_delete" id="0x0108001d" />
+ <public type="layout" name="activity_list_item" id="0x01090000" />
<public type="string" name="cancel" id="0x01040000" />
- <string name="cancel">SomeRidiculouslyUnlikelyStringFour</string>
-</resources>
+</resources> \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_four.xml b/core/tests/ResourceLoaderTests/resources/framework/res/values/values.xml
index 5b30eba5953b..5f6e90cf9e0d 100644
--- a/core/tests/ResourceLoaderTests/resources/res/values/dimen_four.xml
+++ b/core/tests/ResourceLoaderTests/resources/framework/res/values/values.xml
@@ -16,6 +16,5 @@
-->
<resources>
- <public type="dimen" name="app_icon_size" id="0x01050000" />
- <dimen name="app_icon_size">400dp</dimen>
+ <string name="cancel">SomeRidiculouslyUnlikelyString</string>
</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/provider1/assets/asset.txt b/core/tests/ResourceLoaderTests/resources/provider1/assets/asset.txt
new file mode 100644
index 000000000000..6dcd8e419a8c
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider1/assets/asset.txt
@@ -0,0 +1 @@
+One \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider1/assets/loader_asset.txt b/core/tests/ResourceLoaderTests/resources/provider1/assets/loader_asset.txt
new file mode 100644
index 000000000000..0e41ffa475af
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider1/assets/loader_asset.txt
@@ -0,0 +1 @@
+LoaderOne \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapRed.png b/core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_png.png
index 4eb8ca3537ea..4eb8ca3537ea 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapRed.png
+++ b/core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_png.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml b/core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_xml.xml
index 57a8cf1b86de..57a8cf1b86de 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider1/res/drawable-nodpi/drawable_xml.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml b/core/tests/ResourceLoaderTests/resources/provider1/res/layout/layout.xml
index ede3838be8de..ede3838be8de 100644
--- a/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider1/res/layout/layout.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_three.xml b/core/tests/ResourceLoaderTests/resources/provider1/res/values/values.xml
index 82cd6ec7270f..5ef75d5426a0 100644
--- a/core/tests/ResourceLoaderTests/resources/res/values/string_three.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider1/res/values/values.xml
@@ -16,6 +16,9 @@
-->
<resources>
- <public type="string" name="cancel" id="0x01040000" />
- <string name="cancel">SomeRidiculouslyUnlikelyStringThree</string>
-</resources>
+ <dimen name="test">100dp</dimen>
+ <string name="test">One</string>
+
+ <string name="additional">One</string>
+ <public type="string" name="additional" id="0x7f0400fe" />
+</resources> \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider2/assets/asset.txt b/core/tests/ResourceLoaderTests/resources/provider2/assets/asset.txt
new file mode 100644
index 000000000000..5673baa5b53d
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider2/assets/asset.txt
@@ -0,0 +1 @@
+Two \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider2/assets/loader_asset.txt b/core/tests/ResourceLoaderTests/resources/provider2/assets/loader_asset.txt
new file mode 100644
index 000000000000..bca782ec1b2b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider2/assets/loader_asset.txt
@@ -0,0 +1 @@
+LoaderTwo \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png b/core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_png.png
index 671d6d00be31..671d6d00be31 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png
+++ b/core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_png.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml b/core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_xml.xml
index 333fe346998c..333fe346998c 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider2/res/drawable-nodpi/drawable_xml.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml b/core/tests/ResourceLoaderTests/resources/provider2/res/layout/layout.xml
index d8bff90d56d8..d8bff90d56d8 100644
--- a/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider2/res/layout/layout.xml
diff --git a/core/tests/ResourceLoaderTests/resources/provider2/res/values/values.xml b/core/tests/ResourceLoaderTests/resources/provider2/res/values/values.xml
new file mode 100644
index 000000000000..387c51905d8a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider2/res/values/values.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="test">200dp</dimen>
+ <string name="test">Two</string>
+
+ <string name="additional">Two</string>
+ <public type="string" name="additional" id="0x7f0400fe" />
+</resources> \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider3/assets/asset.txt b/core/tests/ResourceLoaderTests/resources/provider3/assets/asset.txt
new file mode 100644
index 000000000000..368c34d3ba04
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider3/assets/asset.txt
@@ -0,0 +1 @@
+Three \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider3/assets/loader_asset.txt b/core/tests/ResourceLoaderTests/resources/provider3/assets/loader_asset.txt
new file mode 100644
index 000000000000..bae8ef79a2ce
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider3/assets/loader_asset.txt
@@ -0,0 +1 @@
+LoaderThree \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png b/core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_png.png
index 5231d175569e..5231d175569e 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png
+++ b/core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_png.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableThree.xml b/core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_xml.xml
index 41095d4a158b..41095d4a158b 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableThree.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider3/res/drawable-nodpi/drawable_xml.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_three.xml b/core/tests/ResourceLoaderTests/resources/provider3/res/layout/layout.xml
index d58d3db12ad4..d58d3db12ad4 100644
--- a/core/tests/ResourceLoaderTests/resources/res/layout/layout_three.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider3/res/layout/layout.xml
diff --git a/core/tests/ResourceLoaderTests/resources/provider3/res/values/values.xml b/core/tests/ResourceLoaderTests/resources/provider3/res/values/values.xml
new file mode 100644
index 000000000000..ab75bfac29c6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider3/res/values/values.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="test">300dp</dimen>
+ <string name="test">Three</string>
+
+ <string name="additional">Three</string>
+ <public type="string" name="additional" id="0x7f0400fe" />
+</resources> \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider4/assets/asset.txt b/core/tests/ResourceLoaderTests/resources/provider4/assets/asset.txt
new file mode 100644
index 000000000000..ad70cdd4ab64
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider4/assets/asset.txt
@@ -0,0 +1 @@
+Four \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/provider4/assets/loader_asset.txt b/core/tests/ResourceLoaderTests/resources/provider4/assets/loader_asset.txt
new file mode 100644
index 000000000000..b75d9963575b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider4/assets/loader_asset.txt
@@ -0,0 +1 @@
+LoaderFour \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapWhite.png b/core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_png.png
index e9a4cfcef316..e9a4cfcef316 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapWhite.png
+++ b/core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_png.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableFour.xml b/core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_xml.xml
index 0623245c6152..0623245c6152 100644
--- a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableFour.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider4/res/drawable-nodpi/drawable_xml.xml
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_four.xml b/core/tests/ResourceLoaderTests/resources/provider4/res/layout/layout.xml
index ab9e26529fe7..ab9e26529fe7 100644
--- a/core/tests/ResourceLoaderTests/resources/res/layout/layout_four.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider4/res/layout/layout.xml
diff --git a/core/tests/ResourceLoaderTests/resources/provider4/res/values/values.xml b/core/tests/ResourceLoaderTests/resources/provider4/res/values/values.xml
new file mode 100644
index 000000000000..896993e9d9a6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/provider4/res/values/values.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="test">400dp</dimen>
+ <string name="test">Four</string>
+
+ <string name="additional">Four</string>
+ <public type="string" name="additional" id="0x7f0400fe" />
+</resources> \ No newline at end of file
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_three.xml b/core/tests/ResourceLoaderTests/resources/provider_additional/res/values/values.xml
index 07a35cedd886..29918d7105ef 100644
--- a/core/tests/ResourceLoaderTests/resources/res/values/dimen_three.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider_additional/res/values/values.xml
@@ -16,6 +16,5 @@
-->
<resources>
- <public type="dimen" name="app_icon_size" id="0x01050000" />
- <dimen name="app_icon_size">300dp</dimen>
+ <public type="string" name="additional" id="0x7f0400fe" />
</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/SplitFour/res/values/string_split.xml b/core/tests/ResourceLoaderTests/resources/provider_stable/res/values/public.xml
index 4759db978dcb..269c40fc2a38 100644
--- a/core/tests/ResourceLoaderTests/splits/SplitFour/res/values/string_split.xml
+++ b/core/tests/ResourceLoaderTests/resources/provider_stable/res/values/public.xml
@@ -16,6 +16,9 @@
-->
<resources>
- <public type="string" name="split_overlaid" id="0x7f040001" />
- <string name="split_overlaid">Split FOUR Overlaid</string>
-</resources>
+ <public type="dimen" name="test" id="0x7f010000" />
+ <public type="drawable" name="drawable_png" id="0x7f020000" />
+ <public type="drawable" name="drawable_xml" id="0x7f020001" />
+ <public type="layout" name="layout" id="0x7f030000" />
+ <public type="string" name="test" id="0x7f040000" />
+</resources> \ No newline at end of file
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
deleted file mode 100644
index a552431e23be..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<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
deleted file mode 100644
index b17ec1c66717..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <public type="dimen" name="app_icon_size" id="0x01050000" />
- <dimen name="app_icon_size">100dp</dimen>
-</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml b/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
deleted file mode 100644
index 570b40aa7a7a..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <public type="dimen" name="app_icon_size" id="0x01050000" />
- <dimen name="app_icon_size">200dp</dimen>
-</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
deleted file mode 100644
index b5b4dfd22231..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<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
deleted file mode 100644
index 4962a07bc8c7..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <public type="layout" name="layout" id="0x7f020000" />
-</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
deleted file mode 100644
index bdd6f58e5824..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<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
deleted file mode 100644
index 4fc52723946e..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<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
deleted file mode 100644
index 3604d7b21cf5..000000000000
--- a/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <public type="string" name="cancel" id="0x01040000" />
- <string name="cancel">SomeRidiculouslyUnlikelyStringTwo</string>
-</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/SplitFour/Android.bp b/core/tests/ResourceLoaderTests/splits/SplitFour/Android.bp
deleted file mode 100644
index eb4d8e1ac7f2..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitFour/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-android_test_helper_app {
- name: "FrameworksResourceLoaderTestsSplitFour"
-}
diff --git a/core/tests/ResourceLoaderTests/splits/SplitOne/Android.bp b/core/tests/ResourceLoaderTests/splits/SplitOne/Android.bp
deleted file mode 100644
index 897897fbf254..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitOne/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-android_test_helper_app {
- name: "FrameworksResourceLoaderTestsSplitOne"
-}
diff --git a/core/tests/ResourceLoaderTests/splits/SplitOne/res/values/string_split.xml b/core/tests/ResourceLoaderTests/splits/SplitOne/res/values/string_split.xml
deleted file mode 100644
index 3c215ebc287c..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitOne/res/values/string_split.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <public type="string" name="split_overlaid" id="0x7f040001" />
- <string name="split_overlaid">Split ONE Overlaid</string>
-</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/SplitThree/Android.bp b/core/tests/ResourceLoaderTests/splits/SplitThree/Android.bp
deleted file mode 100644
index bf98a740cd88..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitThree/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-android_test_helper_app {
- name: "FrameworksResourceLoaderTestsSplitThree"
-}
diff --git a/core/tests/ResourceLoaderTests/splits/SplitThree/res/values/string_spli.xml b/core/tests/ResourceLoaderTests/splits/SplitThree/res/values/string_spli.xml
deleted file mode 100644
index 97682aa1b5cf..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitThree/res/values/string_spli.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <public type="string" name="split_overlaid" id="0x7f040001" />
- <string name="split_overlaid">Split THREE Overlaid</string>
-</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/SplitTwo/Android.bp b/core/tests/ResourceLoaderTests/splits/SplitTwo/Android.bp
deleted file mode 100644
index 4582808934df..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitTwo/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-android_test_helper_app {
- name: "FrameworksResourceLoaderTestsSplitTwo"
-}
diff --git a/core/tests/ResourceLoaderTests/splits/SplitTwo/res/values/string_split.xml b/core/tests/ResourceLoaderTests/splits/SplitTwo/res/values/string_split.xml
deleted file mode 100644
index a367063dd43e..000000000000
--- a/core/tests/ResourceLoaderTests/splits/SplitTwo/res/values/string_split.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<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/DirectoryAssetsProviderTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt
deleted file mode 100644
index afe9d7f19f0d..000000000000
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.res.loader.test
-
-import android.content.res.loader.AssetsProvider
-import android.content.res.loader.DirectoryAssetsProvider
-import android.content.res.loader.ResourcesLoader
-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 DirectoryAssetsProviderTest : ResourceLoaderTestBase() {
-
- @get:Rule
- val testName = TestName()
-
- private lateinit var testDir: File
- private lateinit var assetsProvider: AssetsProvider
- private lateinit var loader: ResourcesLoader
-
- @Before
- fun setUpTestDir() {
- testDir = context.filesDir.resolve("DirectoryAssetsProvider_${testName.methodName}")
- assetsProvider = DirectoryAssetsProvider(testDir)
- loader = ResourcesLoader()
- resources.addLoaders(loader)
- }
-
- @After
- fun deleteTestFiles() {
- testDir.deleteRecursively()
- }
-
- @Test
- fun loadDrawableXml() {
- "nonAssetDrawableOne" writeTo "res/drawable-nodpi-v4/non_asset_drawable.xml"
- val provider = openArsc("nonAssetDrawableOne", assetsProvider)
-
- fun getValue() = (resources.getDrawable(R.drawable.non_asset_drawable) as ColorDrawable)
- .color
-
- assertThat(getValue()).isEqualTo(Color.parseColor("#B2D2F2"))
-
- loader.addProvider(provider)
-
- assertThat(getValue()).isEqualTo(Color.parseColor("#000001"))
- }
-
- @Test
- fun loadDrawableBitmap() {
- "nonAssetBitmapGreen" writeTo "res/drawable-nodpi-v4/non_asset_bitmap.png"
- val provider = openArsc("nonAssetBitmapGreen", assetsProvider)
-
- fun getValue() = (resources.getDrawable(R.drawable.non_asset_bitmap) as BitmapDrawable)
- .bitmap.getColor(0, 0).toArgb()
-
- assertThat(getValue()).isEqualTo(Color.MAGENTA)
-
- loader.addProvider(provider)
-
- assertThat(getValue()).isEqualTo(Color.GREEN)
- }
-
- @Test
- fun loadXml() {
- "layoutOne" writeTo "res/layout/layout.xml"
- val provider = openArsc("layoutOne", assetsProvider)
-
- fun getValue() = resources.getLayout(R.layout.layout).advanceToRoot().name
-
- assertThat(getValue()).isEqualTo("MysteryLayout")
-
- loader.addProvider(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/ResourceLoaderAssetsTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt
deleted file mode 100644
index da5092de0627..000000000000
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.res.loader.test
-
-import android.content.res.AssetManager
-import android.content.res.loader.AssetsProvider
-import android.content.res.loader.DirectoryAssetsProvider
-import android.content.res.loader.ResourcesLoader
-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 ResourceLoaderAssetsTest : 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: AssetsProvider.(String) -> Any? = {
- loadAsset(eq(it), anyInt())
- }
-
- val fromFileDescriptor: AssetsProvider.(String) -> Any? = {
- loadAssetParcelFd(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: AssetsProvider.(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 AssetsProvider.loadAsset() = loadAssetFunction(testPath)
-
- private fun AssetManager.openAsset() = openAssetFunction()
-
- private lateinit var testDir: File
-
- @Before
- fun setUpTestDir() {
- testDir = context.filesDir.resolve("DirectoryAssetsProvider_${testName.methodName}")
- testDir.resolve(testPath).apply { parentFile!!.mkdirs() }.writeText(TEST_TEXT)
- }
-
- @Test
- fun multipleProvidersSearchesBackwards() {
- // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
- val assetsProvider = DirectoryAssetsProvider(testDir)
- val assetProviderWrapper = mock(AssetsProvider::class.java).apply {
- doAnswer { assetsProvider.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
- .`when`(this).loadAsset(anyString(), anyInt())
- doAnswer { assetsProvider.loadAssetParcelFd(it.arguments[0] as String) }
- .`when`(this).loadAssetParcelFd(anyString())
- }
-
- val one = ResourcesProvider.empty(assetProviderWrapper)
- val two = mockProvider {
- doReturn(null).`when`(it).loadAsset()
- }
-
- val loader = ResourcesLoader()
- loader.providers = listOf(one, two)
- resources.addLoaders(loader)
-
- assertOpenedAsset()
- inOrder(two.assetsProvider, one.assetsProvider).apply {
- verify(two.assetsProvider)?.loadAsset()
- verify(one.assetsProvider)?.loadAsset()
- }
- }
-
- @Test
- fun multipleLoadersSearchesBackwards() {
- // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
- val assetsProvider = DirectoryAssetsProvider(testDir)
- val assetProviderWrapper = mock(AssetsProvider::class.java).apply {
- doAnswer { assetsProvider.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
- .`when`(this).loadAsset(anyString(), anyInt())
- doAnswer { assetsProvider.loadAssetParcelFd(it.arguments[0] as String) }
- .`when`(this).loadAssetParcelFd(anyString())
- }
-
- val one = ResourcesProvider.empty(assetProviderWrapper)
- val two = mockProvider {
- doReturn(null).`when`(it).loadAsset()
- }
-
- val loader1 = ResourcesLoader()
- loader1.addProvider(one)
- val loader2 = ResourcesLoader()
- loader2.addProvider(two)
-
- resources.addLoaders(loader1, loader2)
-
- assertOpenedAsset()
- inOrder(two.assetsProvider, one.assetsProvider).apply {
- verify(two.assetsProvider)?.loadAsset()
- verify(one.assetsProvider)?.loadAsset()
- }
- }
-
- @Test(expected = FileNotFoundException::class)
- fun failToFindThrowsFileNotFound() {
- val assetsProvider1 = mock(AssetsProvider::class.java).apply {
- doReturn(null).`when`(this).loadAsset()
- }
- val assetsProvider2 = mock(AssetsProvider::class.java).apply {
- doReturn(null).`when`(this).loadAsset()
- }
-
- val loader = ResourcesLoader()
- val one = ResourcesProvider.empty(assetsProvider1)
- val two = ResourcesProvider.empty(assetsProvider2)
- resources.addLoaders(loader)
- loader.providers = listOf(one, two)
-
- assertOpenedAsset()
- }
-
- @Test
- fun throwingIOExceptionIsSkipped() {
- val assetsProvider1 = DirectoryAssetsProvider(testDir)
- val assetsProvider2 = mock(AssetsProvider::class.java).apply {
- doAnswer { throw IOException() }.`when`(this).loadAsset()
- }
-
- val loader = ResourcesLoader()
- val one = ResourcesProvider.empty(assetsProvider1)
- val two = ResourcesProvider.empty(assetsProvider2)
- resources.addLoaders(loader)
- loader.providers = listOf(one, two)
-
- assertOpenedAsset()
- }
-
- @Test(expected = IllegalStateException::class)
- fun throwingNonIOExceptionCausesFailure() {
- val assetsProvider1 = DirectoryAssetsProvider(testDir)
- val assetsProvider2 = mock(AssetsProvider::class.java).apply {
- doAnswer { throw IllegalStateException() }.`when`(this).loadAsset()
- }
-
- val loader = ResourcesLoader()
- val one = ResourcesProvider.empty(assetsProvider1)
- val two = ResourcesProvider.empty(assetsProvider2)
- resources.addLoaders(loader)
- loader.providers = listOf(one, two)
-
- assertOpenedAsset()
- }
-
- private fun mockProvider(block: (AssetsProvider) -> Unit = {}): ResourcesProvider {
- return ResourcesProvider.empty(mock(AssetsProvider::class.java).apply {
- block.invoke(this)
- })
- }
-
- private fun assertOpenedAsset() {
- assertThat(resources.assets.openAsset()).isEqualTo(TEST_TEXT)
- }
-}
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
index 4c62955e41a5..ec6a605340ae 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
@@ -17,29 +17,58 @@
package android.content.res.loader.test
import android.content.Context
-import android.content.res.AssetManager
+import android.content.res.AssetFileDescriptor
+import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.loader.AssetsProvider
import android.content.res.loader.ResourcesProvider
import android.os.ParcelFileDescriptor
+import android.system.Os
+import android.util.ArrayMap
import androidx.test.InstrumentationRegistry
+import org.json.JSONObject
import org.junit.After
import org.junit.Before
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.mock
import java.io.Closeable
+import java.io.FileOutputStream
+import java.io.File
+import java.io.FileDescriptor
+import java.util.zip.ZipInputStream
abstract class ResourceLoaderTestBase {
+ protected val PROVIDER_ONE: String = "FrameworksResourceLoaderTests_ProviderOne"
+ protected val PROVIDER_TWO: String = "FrameworksResourceLoaderTests_ProviderTwo"
+ protected val PROVIDER_THREE: String = "FrameworksResourceLoaderTests_ProviderThree"
+ protected val PROVIDER_FOUR: String = "FrameworksResourceLoaderTests_ProviderFour"
+ protected val PROVIDER_EMPTY: String = "empty"
+ companion object {
+ /** Converts the map to a stable JSON string representation. */
+ fun mapToString(m: Map<String, String>): String {
+ return JSONObject(ArrayMap<String, String>().apply { putAll(m) }).toString()
+ }
+
+ /** Creates a lambda that runs multiple resources queries and concatenates the results. */
+ fun query(queries: Map<String, (Resources) -> String>): Resources.() -> String {
+ return {
+ val resultMap = ArrayMap<String, String>()
+ queries.forEach { q ->
+ resultMap[q.key] = try {
+ q.value.invoke(this)
+ } catch (e: Exception) {
+ e.javaClass.simpleName
+ }
+ }
+ mapToString(resultMap)
+ }
+ }
+ }
+
+ // Data type of the current test iteration
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
+ protected lateinit var resources: Resources
// Track opened streams and ResourcesProviders to close them after testing
private val openedObjects = mutableListOf<Closeable>()
@@ -47,6 +76,8 @@ abstract class ResourceLoaderTestBase {
@Before
fun setUpBase() {
context = InstrumentationRegistry.getTargetContext()
+ .createConfigurationContext(Configuration())
+ resources = context.resources
}
@After
@@ -61,82 +92,207 @@ abstract class ResourceLoaderTestBase {
}
}
- protected fun String.openProvider(
- dataType: DataType = this@ResourceLoaderTestBase.dataType
- ): ResourcesProvider = when (dataType) {
- DataType.APK -> {
- context.copiedRawFile("${this}Apk").use {
- ResourcesProvider.loadFromApk(it, mock(AssetsProvider::class.java))
- }.also { openedObjects += it }
+ protected fun String.openProvider(dataType: DataType,
+ assetsProvider: MemoryAssetsProvider?): ResourcesProvider {
+ if (assetsProvider != null) {
+ openedObjects += assetsProvider
}
- DataType.ARSC -> {
- openArsc(this, mock(AssetsProvider::class.java))
- }
- DataType.SPLIT -> {
- ResourcesProvider.loadFromSplit(context, this)
+ return when (dataType) {
+ DataType.APK_DISK_FD -> {
+ val file = context.copiedAssetFile("$this.apk")
+ ResourcesProvider.loadFromApk(ParcelFileDescriptor.fromFd(file.fd),
+ assetsProvider).apply {
+ file.close()
+ }
+ }
+ DataType.APK_DISK_FD_OFFSETS -> {
+ val asset = context.assets.openFd("$this.apk")
+ ResourcesProvider.loadFromApk(asset.parcelFileDescriptor, asset.startOffset,
+ asset.length, assetsProvider).apply {
+ asset.close()
+ }
+ }
+ DataType.ARSC_DISK_FD -> {
+ val file = context.copiedAssetFile("$this.arsc")
+ ResourcesProvider.loadFromTable(ParcelFileDescriptor.fromFd(file.fd),
+ assetsProvider).apply {
+ file.close()
+ }
+ }
+ DataType.ARSC_DISK_FD_OFFSETS -> {
+ val asset = context.assets.openFd("$this.arsc")
+ ResourcesProvider.loadFromTable(asset.parcelFileDescriptor, asset.startOffset,
+ asset.length, assetsProvider).apply {
+ asset.close()
+ }
+ }
+ DataType.APK_RAM_OFFSETS -> {
+ val asset = context.assets.openFd("$this.apk")
+ val leadingGarbageSize = 100L
+ val trailingGarbageSize = 55L
+ val fd = loadAssetIntoMemory(asset, leadingGarbageSize.toInt(),
+ trailingGarbageSize.toInt())
+ ResourcesProvider.loadFromApk(fd, leadingGarbageSize, asset.declaredLength,
+ assetsProvider).apply {
+ asset.close()
+ fd.close()
+ }
+ }
+ DataType.APK_RAM_FD -> {
+ val asset = context.assets.openFd("$this.apk")
+ var fd = loadAssetIntoMemory(asset)
+ ResourcesProvider.loadFromApk(fd, assetsProvider).apply {
+ asset.close()
+ fd.close()
+ }
+ }
+ DataType.ARSC_RAM_MEMORY -> {
+ val asset = context.assets.openFd("$this.arsc")
+ var fd = loadAssetIntoMemory(asset)
+ ResourcesProvider.loadFromTable(fd, assetsProvider).apply {
+ asset.close()
+ fd.close()
+ }
+ }
+ DataType.ARSC_RAM_MEMORY_OFFSETS -> {
+ val asset = context.assets.openFd("$this.arsc")
+ val leadingGarbageSize = 100L
+ val trailingGarbageSize = 55L
+ val fd = loadAssetIntoMemory(asset, leadingGarbageSize.toInt(),
+ trailingGarbageSize.toInt())
+ ResourcesProvider.loadFromTable(fd, leadingGarbageSize, asset.declaredLength,
+ assetsProvider).apply {
+ asset.close()
+ fd.close()
+ }
+ }
+ DataType.EMPTY -> {
+ if (equals(PROVIDER_EMPTY)) {
+ ResourcesProvider.empty(EmptyAssetsProvider())
+ } else {
+ if (assetsProvider == null) ResourcesProvider.empty(ZipAssetsProvider(this))
+ else ResourcesProvider.empty(assetsProvider)
+ }
+ }
+ DataType.DIRECTORY -> {
+ ResourcesProvider.loadFromDirectory(zipToDir("$this.apk").absolutePath,
+ assetsProvider)
+ }
+ DataType.SPLIT -> {
+ ResourcesProvider.loadFromSplit(context, "${this}_Split")
+ }
}
- DataType.ASSET -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer { byteInputStream() }.`when`(assetsProvider)
- .loadAsset(eq("assets/Asset.txt"), anyInt())
- ResourcesProvider.empty(assetsProvider)
+ }
+
+ class EmptyAssetsProvider : AssetsProvider
+
+ /** An AssetsProvider that reads from a zip asset. */
+ inner class ZipAssetsProvider(val providerName: String) : AssetsProvider {
+ val root: File = zipToDir("$providerName.apk")
+
+ override fun loadAssetFd(path: String, accessMode: Int): AssetFileDescriptor? {
+ val f = File(root, path)
+ return if (f.exists()) AssetFileDescriptor(
+ ParcelFileDescriptor.open(File(root, path),
+ ParcelFileDescriptor.MODE_READ_ONLY), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH) else null
}
- DataType.ASSET_FD -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer {
- val file = context.filesDir.resolve("Asset.txt")
- file.writeText(this)
- ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
- }.`when`(assetsProvider).loadAssetParcelFd("assets/Asset.txt")
- ResourcesProvider.empty(assetsProvider)
+ }
+
+ /** AssetsProvider for testing that returns file descriptors to files in RAM. */
+ class MemoryAssetsProvider : AssetsProvider, Closeable {
+ var loadAssetResults = HashMap<String, FileDescriptor>()
+
+ fun addLoadAssetFdResult(path: String, value: String) = apply {
+ val fd = Os.memfd_create(path, 0)
+ val valueBytes = value.toByteArray()
+ Os.write(fd, valueBytes, 0, valueBytes.size)
+ loadAssetResults[path] = fd
}
- DataType.NON_ASSET -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer {
- val file = context.filesDir.resolve("NonAsset.txt")
- file.writeText(this)
- ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
- }.`when`(assetsProvider).loadAssetParcelFd("NonAsset.txt")
- ResourcesProvider.empty(assetsProvider)
+
+ override fun loadAssetFd(path: String, accessMode: Int): AssetFileDescriptor? {
+ return if (loadAssetResults.containsKey(path)) AssetFileDescriptor(
+ ParcelFileDescriptor.dup(loadAssetResults[path]), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH) else null
}
- DataType.NON_ASSET_DRAWABLE -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer { context.copiedRawFile(this) }.`when`(assetsProvider)
- .loadAssetParcelFd("res/drawable-nodpi-v4/non_asset_drawable.xml")
- openArsc(this, assetsProvider)
+
+ override fun close() {
+ for (f in loadAssetResults.values) {
+ Os.close(f)
+ }
}
- DataType.NON_ASSET_BITMAP -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer { resources.openRawResource(rawFile(this)) }
- .`when`(assetsProvider)
- .loadAsset(eq("res/drawable-nodpi-v4/non_asset_bitmap.png"), anyInt())
- openArsc(this, assetsProvider)
+ }
+
+ /** Extracts an archive-based asset into a directory on disk. */
+ private fun zipToDir(name: String): File {
+ val root = File(context.filesDir, name.split('.')[0])
+ if (root.exists()) {
+ return root
}
- DataType.NON_ASSET_LAYOUT -> {
- val assetsProvider = mock(AssetsProvider::class.java)
- doAnswer { resources.openRawResource(rawFile(this)) }.`when`(assetsProvider)
- .loadAsset(eq("res/layout/layout.xml"), anyInt())
- doAnswer { context.copiedRawFile(this) }.`when`(assetsProvider)
- .loadAssetParcelFd("res/layout/layout.xml")
- openArsc(this, assetsProvider)
+
+ root.mkdir()
+ ZipInputStream(context.assets.open(name)).use { zis ->
+ while (true) {
+ val entry = zis.nextEntry ?: break
+ val file = File(root, entry.name)
+ if (entry.isDirectory) {
+ continue
+ }
+
+ file.parentFile.mkdirs()
+ file.outputStream().use { output ->
+ var b = zis.read()
+ while (b != -1) {
+ output.write(b)
+ b = zis.read()
+ }
+ }
+ }
}
+ return root
}
- protected fun openArsc(rawName: String, assetsProvider: AssetsProvider): ResourcesProvider {
- return context.copiedRawFile("${rawName}Arsc")
- .use { ResourcesProvider.loadFromTable(it, assetsProvider) }
- .also { openedObjects += it }
+ /** Loads the asset into a temporary file stored in RAM. */
+ private fun loadAssetIntoMemory(
+ asset: AssetFileDescriptor,
+ leadingGarbageSize: Int = 0,
+ trailingGarbageSize: Int = 0
+ ): ParcelFileDescriptor {
+ val originalFd = Os.memfd_create(asset.toString(), 0 /* flags */)
+ val fd = ParcelFileDescriptor.dup(originalFd)
+ Os.close(originalFd)
+
+ val input = asset.createInputStream()
+ FileOutputStream(fd.fileDescriptor).use { output ->
+ // Add garbage before the APK data
+ for (i in 0 until leadingGarbageSize) {
+ output.write(Math.random().toInt())
+ }
+
+ for (i in 0 until asset.length.toInt()) {
+ output.write(input.read())
+ }
+
+ // Add garbage after the APK data
+ for (i in 0 until trailingGarbageSize) {
+ output.write(Math.random().toInt())
+ }
+ }
+
+ return fd
}
enum class DataType {
- APK,
- ARSC,
- SPLIT,
- ASSET,
- ASSET_FD,
- NON_ASSET,
- NON_ASSET_DRAWABLE,
- NON_ASSET_BITMAP,
- NON_ASSET_LAYOUT,
+ APK_DISK_FD,
+ APK_DISK_FD_OFFSETS,
+ APK_RAM_FD,
+ APK_RAM_OFFSETS,
+ ARSC_DISK_FD,
+ ARSC_DISK_FD_OFFSETS,
+ ARSC_RAM_MEMORY,
+ ARSC_RAM_MEMORY_OFFSETS,
+ EMPTY,
+ DIRECTORY,
+ SPLIT
}
}
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
index 16eafcd451c2..5aa8814c7481 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
@@ -19,6 +19,7 @@ package android.content.res.loader.test
import android.app.Activity
import android.content.Context
import android.content.Intent
+import android.content.res.AssetManager
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.loader.ResourcesLoader
@@ -54,98 +55,177 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
fun parameters(): Array<Any> {
val parameters = mutableListOf<Parameter>()
- // R.string
+ // Test resolution of resources encoded within the resources.arsc.
parameters += Parameter(
- { getString(android.R.string.cancel) },
- "stringOne", { "SomeRidiculouslyUnlikelyStringOne" },
- "stringTwo", { "SomeRidiculouslyUnlikelyStringTwo" },
- "stringThree", { "SomeRidiculouslyUnlikelyStringThree" },
- "stringFour", { "SomeRidiculouslyUnlikelyStringFour" },
- listOf(DataType.APK, DataType.ARSC)
+ "tableBased",
+ query(mapOf(
+ "getOverlaid" to { res ->
+ res.getString(R.string.test)
+ },
+ "getAdditional" to { res ->
+ res.getString(0x7f0400fe /* R.string.additional */)
+ },
+ "getIdentifier" to { res ->
+ res.getString(res.getIdentifier("test", "string",
+ "android.content.res.loader.test"))
+ },
+ "getIdentifierAdditional" to { res ->
+ res.getString(res.getIdentifier("additional", "string",
+ "android.content.res.loader.test"))
+ }
+ )),
+ mapOf("getOverlaid" to "Not overlaid",
+ "getAdditional" to "NotFoundException",
+ "getIdentifier" to "Not overlaid",
+ "getIdentifierAdditional" to "NotFoundException"),
+
+ mapOf("getOverlaid" to "One",
+ "getAdditional" to "One",
+ "getIdentifier" to "One",
+ "getIdentifierAdditional" to "One"),
+
+ mapOf("getOverlaid" to "Two",
+ "getAdditional" to "Two",
+ "getIdentifier" to "Two",
+ "getIdentifierAdditional" to "Two"),
+
+ mapOf("getOverlaid" to "Three",
+ "getAdditional" to "Three",
+ "getIdentifier" to "Three",
+ "getIdentifierAdditional" to "Three"),
+
+ mapOf("getOverlaid" to "Four",
+ "getAdditional" to "Four",
+ "getIdentifier" to "Four",
+ "getIdentifierAdditional" to "Four"),
+ listOf(DataType.APK_DISK_FD, DataType.APK_DISK_FD_OFFSETS, DataType.APK_RAM_FD,
+ DataType.APK_RAM_OFFSETS, DataType.ARSC_DISK_FD,
+ DataType.ARSC_DISK_FD_OFFSETS, DataType.ARSC_RAM_MEMORY,
+ DataType.ARSC_RAM_MEMORY_OFFSETS, DataType.SPLIT, DataType.DIRECTORY)
)
- // R.dimen
+ // Test resolution of file-based resources and assets with no assets provider.
parameters += Parameter(
- { getDimensionPixelSize(android.R.dimen.app_icon_size) },
- "dimenOne", { 100.dpToPx(resources) },
- "dimenTwo", { 200.dpToPx(resources) },
- "dimenThree", { 300.dpToPx(resources) },
- "dimenFour", { 400.dpToPx(resources) },
- listOf(DataType.APK, DataType.ARSC)
+ "tableFileBased",
+ query(mapOf(
+ // Drawable xml in res directory
+ "drawableXml" to { res ->
+ (res.getDrawable(R.drawable.drawable_xml) as ColorDrawable)
+ .color.toString()
+ },
+ // Asset as compiled XML layout in res directory
+ "layout" to { res ->
+ res.getLayout(R.layout.layout).advanceToRoot().name
+ },
+ // Bitmap drawable in res directory
+ "drawablePng" to { res ->
+ (res.getDrawable(R.drawable.drawable_png) as BitmapDrawable)
+ .bitmap.getColor(0, 0).toArgb().toString()
+ }
+ )),
+ mapOf("drawableXml" to Color.parseColor("#B2D2F2").toString(),
+ "layout" to "MysteryLayout",
+ "drawablePng" to Color.parseColor("#FF00FF").toString()),
+
+ mapOf("drawableXml" to Color.parseColor("#000001").toString(),
+ "layout" to "RelativeLayout",
+ "drawablePng" to Color.RED.toString()),
+
+ mapOf("drawableXml" to Color.parseColor("#000002").toString(),
+ "layout" to "LinearLayout",
+ "drawablePng" to Color.GREEN.toString()),
+
+ mapOf("drawableXml" to Color.parseColor("#000003").toString(),
+ "layout" to "FrameLayout",
+ "drawablePng" to Color.BLUE.toString()),
+
+ mapOf("drawableXml" to Color.parseColor("#000004").toString(),
+ "layout" to "TableLayout",
+ "drawablePng" to Color.WHITE.toString()),
+ listOf(DataType.APK_DISK_FD, DataType.APK_DISK_FD_OFFSETS, DataType.APK_RAM_FD,
+ DataType.APK_RAM_OFFSETS, DataType.SPLIT, DataType.DIRECTORY)
)
- // File in the assets directory
+ // Test resolution of assets.
parameters += Parameter(
- { assets.open("Asset.txt").reader().readText() },
- "assetOne", { "assetOne" },
- "assetTwo", { "assetTwo" },
- "assetFour", { "assetFour" },
- "assetThree", { "assetThree" },
- listOf(DataType.ASSET)
+ "fileBased",
+ query(mapOf(
+ // File in the assets directory
+ "openAsset" to { res ->
+ res.assets.open("asset.txt").reader().readText()
+ },
+ // From assets directory returning file descriptor
+ "openAssetFd" to { res ->
+ res.assets.openFd("asset.txt").readText()
+ },
+ // Asset as compiled XML layout in res directory
+ "layout" to { res ->
+ res.assets.openXmlResourceParser("res/layout/layout.xml")
+ .advanceToRoot().name
+ }
+ )),
+ mapOf("openAsset" to "In assets directory",
+ "openAssetFd" to "In assets directory",
+ "layout" to "MysteryLayout"),
+
+ mapOf("openAsset" to "One",
+ "openAssetFd" to "One",
+ "layout" to "RelativeLayout"),
+
+ mapOf("openAsset" to "Two",
+ "openAssetFd" to "Two",
+ "layout" to "LinearLayout"),
+
+ mapOf("openAsset" to "Three",
+ "openAssetFd" to "Three",
+ "layout" to "FrameLayout"),
+
+ mapOf("openAsset" to "Four",
+ "openAssetFd" to "Four",
+ "layout" to "TableLayout"),
+ listOf(DataType.EMPTY)
)
- // From assets directory returning file descriptor
+ // Test assets from apk and provider
parameters += Parameter(
- { assets.openFd("Asset.txt").readText() },
- "assetOne", { "assetOne" },
- "assetTwo", { "assetTwo" },
- "assetFour", { "assetFour" },
- "assetThree", { "assetThree" },
- listOf(DataType.ASSET_FD)
- )
-
- // From root directory returning file descriptor
- parameters += Parameter(
- { assets.openNonAssetFd("NonAsset.txt").readText() },
- "NonAssetOne", { "NonAssetOne" },
- "NonAssetTwo", { "NonAssetTwo" },
- "NonAssetThree", { "NonAssetThree" },
- "NonAssetFour", { "NonAssetFour" },
- listOf(DataType.NON_ASSET)
- )
+ "fileBasedApkAssetsProvider",
+ query(mapOf(
+ // File in the assets directory
+ "openAsset" to { res ->
+ res.assets.open("asset.txt").reader().readText()
+ },
+ // From assets directory returning file descriptor
+ "openAssetFd" to { res ->
+ res.assets.openFd("asset.txt").readText()
+ }
+ )),
+ mapOf("openAsset" to "In assets directory",
+ "openAssetFd" to "In assets directory"),
+
+ mapOf("openAsset" to "AssetsOne",
+ "openAssetFd" to "AssetsOne"),
+ { MemoryAssetsProvider().addLoadAssetFdResult("assets/asset.txt",
+ "AssetsOne") },
+
+ mapOf("openAsset" to "Two",
+ "openAssetFd" to "Two"),
+ null /* assetProviderTwo */,
+
+ mapOf("openAsset" to "AssetsThree",
+ "openAssetFd" to "AssetsThree"),
+ { MemoryAssetsProvider().addLoadAssetFdResult("assets/asset.txt",
+ "AssetsThree") },
+
+ mapOf("openAsset" to "Four",
+ "openAssetFd" to "Four"),
+ null /* assetProviderFour */,
+ listOf(DataType.APK_DISK_FD, DataType.APK_DISK_FD_OFFSETS, DataType.APK_RAM_FD,
+ DataType.APK_RAM_OFFSETS, DataType.DIRECTORY)
- // Asset as compiled XML drawable
- parameters += Parameter(
- { (getDrawable(R.drawable.non_asset_drawable) as ColorDrawable).color },
- "nonAssetDrawableOne", { Color.parseColor("#000001") },
- "nonAssetDrawableTwo", { Color.parseColor("#000002") },
- "nonAssetDrawableThree", { Color.parseColor("#000003") },
- "nonAssetDrawableFour", { Color.parseColor("#000004") },
- 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()
- },
- "nonAssetBitmapRed", { Color.RED },
- "nonAssetBitmapGreen", { Color.GREEN },
- "nonAssetBitmapBlue", { Color.BLUE },
- "nonAssetBitmapWhite", { Color.WHITE },
- listOf(DataType.NON_ASSET_BITMAP)
- )
-
- // Asset as compiled XML layout
- parameters += Parameter(
- { getLayout(R.layout.layout).advanceToRoot().name },
- "layoutOne", { "RelativeLayout" },
- "layoutTwo", { "LinearLayout" },
- "layoutThree", { "FrameLayout" },
- "layoutFour", { "TableLayout" },
- 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" },
- "split_three", { "Split THREE Overlaid" },
- "split_four", { "Split FOUR Overlaid" },
- listOf(DataType.SPLIT)
- )
+ // TODO(151949807): Increase testing for cookie based APIs and for what happens when
+ // some providers do not overlay base resources
return parameters.flatMap { parameter ->
parameter.dataTypes.map { dataType ->
@@ -162,23 +242,31 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
@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 val valueThree by lazy { parameter.valueThree(this) }
- private val valueFour by lazy { parameter.valueFour(this) }
-
- private fun openOne() = parameter.providerOne.openProvider()
- private fun openTwo() = parameter.providerTwo.openProvider()
- private fun openThree() = parameter.providerThree.openProvider()
- private fun openFour() = parameter.providerFour.openProvider()
+ private val valueOriginal by lazy { mapToString(parameter.valueOriginal) }
+ private val valueOne by lazy { mapToString(parameter.valueOne) }
+ private val valueTwo by lazy { mapToString(parameter.valueTwo) }
+ private val valueThree by lazy { mapToString(parameter.valueThree) }
+ private val valueFour by lazy { mapToString(parameter.valueFour) }
+
+ private fun openOne() = PROVIDER_ONE.openProvider(dataType,
+ parameter.assetProviderOne?.invoke())
+ private fun openTwo() = PROVIDER_TWO.openProvider(dataType,
+ parameter.assetProviderTwo?.invoke())
+ private fun openThree() = PROVIDER_THREE.openProvider(dataType,
+ parameter.assetProviderThree?.invoke())
+ private fun openFour() = PROVIDER_FOUR.openProvider(dataType,
+ parameter.assetProviderFour?.invoke())
+ private fun openEmpty() = PROVIDER_EMPTY.openProvider(DataType.EMPTY, null)
// Class method for syntax highlighting purposes
private fun getValue(c: Context = context) = parameter.getValue(c.resources)
+ private fun getValue(r: Resources) = parameter.getValue(r)
@Test
fun assertValueUniqueness() {
// Ensure the parameters are valid in case of coding errors
val original = getValue()
+ assertEquals(valueOriginal, original)
assertNotEquals(valueOne, original)
assertNotEquals(valueTwo, original)
assertNotEquals(valueThree, original)
@@ -193,7 +281,6 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
@Test
fun addProvidersRepeatedly() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader = ResourcesLoader()
@@ -209,12 +296,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueTwo, getValue())
loader.removeProvider(testTwo)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
}
@Test
fun addLoadersRepeatedly() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader1 = ResourcesLoader()
@@ -232,12 +318,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueTwo, getValue())
resources.removeLoaders(loader2)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
}
@Test
fun setMultipleProviders() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader = ResourcesLoader()
@@ -250,12 +335,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueOne, getValue())
loader.providers = Collections.emptyList()
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
}
@Test
fun addMultipleLoaders() {
- val originalValue = getValue()
val loader1 = ResourcesLoader()
loader1.addProvider(openOne())
val loader2 = ResourcesLoader()
@@ -268,7 +352,28 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueOne, getValue())
resources.removeLoaders(loader1)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
+ }
+
+ @Test
+ fun emptyProvider() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+ val testEmpty = openEmpty()
+ val loader = ResourcesLoader()
+
+ resources.addLoaders(loader)
+ loader.providers = listOf(testOne, testEmpty, testTwo)
+ assertEquals(valueTwo, getValue())
+
+ loader.removeProvider(testTwo)
+ assertEquals(valueOne, getValue())
+
+ loader.removeProvider(testOne)
+ assertEquals(valueOriginal, getValue())
+
+ loader.providers = Collections.emptyList()
+ assertEquals(valueOriginal, getValue())
}
@Test(expected = UnsupportedOperationException::class)
@@ -385,7 +490,6 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
@Test
fun reorderProviders() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader = ResourcesLoader()
@@ -405,12 +509,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueOne, getValue())
loader.removeProvider(testOne)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
}
@Test
fun reorderLoaders() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader1 = ResourcesLoader()
@@ -432,7 +535,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
assertEquals(valueOne, getValue())
resources.removeLoaders(loader1)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
}
@Test
@@ -460,6 +563,9 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
loader1.removeProvider(testOne)
assertEquals(valueFour, getValue())
+
+ loader2.removeProvider(testFour)
+ assertEquals(valueThree, getValue())
}
private fun createContext(context: Context, id: Int): Context {
@@ -470,7 +576,6 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
@Test
fun copyContextLoaders() {
- val originalValue = getValue()
val loader1 = ResourcesLoader()
loader1.addProvider(openOne())
val loader2 = ResourcesLoader()
@@ -490,13 +595,13 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
// Changing the loaders of the original context should not affect the child context.
resources.removeLoaders(loader1)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
assertEquals(valueTwo, getValue(childContext))
// A new context created from the original after an update to the original's loaders should
// have the updated loaders.
val originalPrime = createContext(context, 2)
- assertEquals(originalValue, getValue(originalPrime))
+ assertEquals(valueOriginal, getValue(originalPrime))
// A new context created from the child context after an update to the child's loaders
// should have the updated loaders.
@@ -506,7 +611,6 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
@Test
fun loaderUpdatesAffectContexts() {
- val originalValue = getValue()
val testOne = openOne()
val testTwo = openTwo()
val loader = ResourcesLoader()
@@ -525,15 +629,15 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
// Changes to the loaders for a context do not affect providers.
resources.clearLoaders()
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
assertEquals(valueTwo, getValue(childContext))
val childContext2 = createContext(context, 1)
- assertEquals(originalValue, getValue())
- assertEquals(originalValue, getValue(childContext2))
+ assertEquals(valueOriginal, getValue())
+ assertEquals(valueOriginal, getValue(childContext2))
childContext2.resources.addLoaders(loader)
- assertEquals(originalValue, getValue())
+ assertEquals(valueOriginal, getValue())
assertEquals(valueTwo, getValue(childContext))
assertEquals(valueTwo, getValue(childContext2))
}
@@ -629,22 +733,82 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
provider.close()
}
+ @Test
+ fun addLoadersRepeatedlyCustomResources() {
+ val res = Resources(AssetManager::class.java.newInstance(), resources.displayMetrics,
+ resources.configuration!!)
+ val originalValue = getValue(res)
+ val testOne = openOne()
+ val testTwo = openTwo()
+ val loader1 = ResourcesLoader()
+ val loader2 = ResourcesLoader()
+
+ res.addLoaders(loader1)
+ loader1.addProvider(testOne)
+ assertEquals(valueOne, getValue(res))
+
+ res.addLoaders(loader2)
+ loader2.addProvider(testTwo)
+ assertEquals(valueTwo, getValue(res))
+
+ res.removeLoaders(loader1)
+ res.addLoaders(loader1)
+ assertEquals(valueOne, getValue(res))
+
+ res.removeLoaders(loader1)
+ assertEquals(valueTwo, getValue(res))
+
+ res.removeLoaders(loader2)
+ assertEquals(originalValue, getValue(res))
+ }
+
+ @Test
+ fun setMultipleProvidersCustomResources() {
+ val res = Resources(AssetManager::class.java.newInstance(), resources.displayMetrics,
+ resources.configuration!!)
+ val originalValue = getValue(res)
+ val testOne = openOne()
+ val testTwo = openTwo()
+ val loader = ResourcesLoader()
+
+ res.addLoaders(loader)
+ loader.providers = listOf(testOne, testTwo)
+ assertEquals(valueTwo, getValue(res))
+
+ loader.removeProvider(testTwo)
+ assertEquals(valueOne, getValue(res))
+
+ loader.providers = Collections.emptyList()
+ assertEquals(originalValue, getValue(res))
+ }
+
data class Parameter(
- val getValue: Resources.() -> Any,
- val providerOne: String,
- val valueOne: ResourceLoaderValuesTest.() -> Any,
- val providerTwo: String,
- val valueTwo: ResourceLoaderValuesTest.() -> Any,
- val providerThree: String,
- val valueThree: ResourceLoaderValuesTest.() -> Any,
- val providerFour: String,
- val valueFour: ResourceLoaderValuesTest.() -> Any,
+ val testPrefix: String,
+ val getValue: Resources.() -> String,
+ val valueOriginal: Map<String, String>,
+ val valueOne: Map<String, String>,
+ val assetProviderOne: (() -> MemoryAssetsProvider)? = null,
+ val valueTwo: Map<String, String>,
+ val assetProviderTwo: (() -> MemoryAssetsProvider)? = null,
+ val valueThree: Map<String, String>,
+ val assetProviderThree: (() -> MemoryAssetsProvider)? = null,
+ val valueFour: Map<String, String>,
+ val assetProviderFour: (() -> MemoryAssetsProvider)? = null,
val dataTypes: List<DataType>
) {
- override fun toString(): String {
- val prefix = providerOne.commonPrefixWith(providerTwo)
- return "$prefix${providerOne.removePrefix(prefix)}|${providerTwo.removePrefix(prefix)}"
- }
+ constructor(
+ testPrefix: String,
+ getValue: Resources.() -> String,
+ valueOriginal: Map<String, String>,
+ valueOne: Map<String, String>,
+ valueTwo: Map<String, String>,
+ valueThree: Map<String, String>,
+ valueFour: Map<String, String>,
+ dataTypes: List<DataType>
+ ): this(testPrefix, getValue, valueOriginal, valueOne,
+ null, valueTwo, null, valueThree, null, valueFour, null, dataTypes)
+
+ override fun toString() = testPrefix
}
}
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
index 4e8ee5cf3c3b..526160d04000 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
@@ -44,25 +44,20 @@ fun Int.dpToPx(resources: Resources) = TypedValue.applyDimension(
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 ->
+fun Context.copiedAssetFile(fileName: String): ParcelFileDescriptor {
+ return resources.assets.open(fileName).use { input ->
// 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)
- }
+ copiedFile.outputStream().use { output ->
+ input.copyTo(output)
}
-
ParcelFileDescriptor.open(copiedFile, ParcelFileDescriptor.MODE_READ_WRITE)
}
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index b2b0ec2a54f8..918e7af12d31 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -21,6 +21,7 @@
#include "android-base/errors.h"
#include "android-base/file.h"
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
@@ -40,29 +41,342 @@ using base::unique_fd;
static const std::string kResourcesArsc("resources.arsc");
-ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
- const std::string& path,
+ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
+ std::string path,
time_t last_mod_time,
package_property_t property_flags)
- : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
+ : assets_provider_(std::move(assets_provider)),
+ path_(std::move(path)),
+ last_mod_time_(last_mod_time),
property_flags_(property_flags) {
}
-std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
- bool for_loader) {
- package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
- (for_loader ? PROPERTY_LOADER : 0U);
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
+// Provides asset files from a zip file.
+class ZipAssetsProvider : public AssetsProvider {
+ public:
+ ~ZipAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
+ ::ZipArchiveHandle unmanaged_handle;
+ const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ ::CloseArchive(unmanaged_handle);
+ return {};
+ }
+
+ return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
+ }
+
+ static std::unique_ptr<const AssetsProvider> Create(
+ unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
+ const off64_t length = ApkAssets::kUnknownLength) {
+
+ ::ZipArchiveHandle unmanaged_handle;
+ const int32_t result = (length == ApkAssets::kUnknownLength)
+ ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
+ : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
+ offset);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << length << ": " << ::ErrorCodeString(result);
+ ::CloseArchive(unmanaged_handle);
+ return {};
+ }
+
+ return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
+ unmanaged_handle));
+ }
+
+ // Iterate over all files and directories within the zip. The order of iteration is not
+ // guaranteed to be the same as the order of elements in the central directory but is stable for a
+ // given zip file.
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override {
+ // 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() != '/') {
+ root_path_full += '/';
+ }
+
+ void* cookie;
+ if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+ return false;
+ }
+
+ std::string name;
+ ::ZipEntry entry{};
+
+ // We need to hold back directories because many paths will contain them and we want to only
+ // surface one.
+ std::set<std::string> dirs{};
+
+ int32_t result;
+ while ((result = ::Next(cookie, &entry, &name)) == 0) {
+ StringPiece full_file_path(name);
+ StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
+ }
+ }
+ ::EndIteration(cookie);
+
+ // Now present the unique directories.
+ for (const std::string& dir : dirs) {
+ f(dir, kFileTypeDirectory);
+ }
+
+ // -1 is end of iteration, anything else is an error.
+ return result == -1;
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
+ if (file_exists) {
+ *file_exists = false;
+ }
+
+ ::ZipEntry entry;
+ int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
+ if (result != 0) {
+ return {};
+ }
+
+ if (file_exists) {
+ *file_exists = true;
+ }
+
+ const int fd = ::GetFileDescriptor(zip_handle_.get());
+ const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ if (entry.method == kCompressDeflated) {
+ std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
+ if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
+ true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+
+ std::unique_ptr<Asset> asset =
+ Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
+ } else {
+ std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
+ if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
+ true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+
+ unique_fd ufd;
+ if (!GetPath()) {
+ // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
+ // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
+ // to create new file descriptors.
+ ufd = unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ }
+
+ std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
+ std::move(ufd), mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
+
+ explicit ZipAssetsProvider(std::string path,
+ std::string friendly_name,
+ ZipArchiveHandle unmanaged_handle)
+ : zip_handle_(unmanaged_handle, ::CloseArchive),
+ path_(std::move(path)),
+ friendly_name_(std::move(friendly_name)) { }
+
+ const char* GetPath() const {
+ return path_.empty() ? nullptr : path_.c_str();
+ }
+
+ using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
+ ZipArchivePtr zip_handle_;
+ std::string path_;
+ std::string friendly_name_;
+};
+
+class DirectoryAssetsProvider : AssetsProvider {
+ public:
+ ~DirectoryAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
+ struct stat sb{};
+ const int result = stat(path.c_str(), &sb);
+ if (result == -1) {
+ LOG(ERROR) << "Failed to find directory '" << path << "'.";
+ return nullptr;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ LOG(ERROR) << "Path '" << path << "' is not a directory.";
+ return nullptr;
+ }
+
+ return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
+ const std::string resolved_path = ResolvePath(path);
+ if (file_exists) {
+ struct stat sb{};
+ const int result = stat(resolved_path.c_str(), &sb);
+ *file_exists = result != -1 && S_ISREG(sb.st_mode);
+ }
+
+ return ApkAssets::CreateAssetFromFile(resolved_path);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
+
+ explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
+
+ inline std::string ResolvePath(const std::string& path) const {
+ return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
+ }
+
+ const std::string path_;
+};
+
+// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
+class EmptyAssetsProvider : public AssetsProvider {
+ public:
+ EmptyAssetsProvider() = default;
+ ~EmptyAssetsProvider() override = default;
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const override {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
+};
+
+// AssetProvider implementation
+class MultiAssetsProvider : public AssetsProvider {
+ public:
+ ~MultiAssetsProvider() override = default;
+
+ static std::unique_ptr<const AssetsProvider> Create(
+ std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
+ CHECK(parent != nullptr) << "parent provider must not be null";
+ return (!child) ? std::move(parent)
+ : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
+ std::move(child), std::move(parent)));
+ }
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override {
+ // TODO: Only call the function once for files defined in the parent and child
+ return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
+ }
+
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
+ auto asset = child_->Open(path, mode, file_exists);
+ return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
+
+ MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
+ std::unique_ptr<const AssetsProvider> parent)
+ : child_(std::move(child)), parent_(std::move(parent)) { }
+
+ std::unique_ptr<const AssetsProvider> child_;
+ std::unique_ptr<const AssetsProvider> parent_;
+};
+
+// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
+// file.
+std::unique_ptr<const ApkAssets> ApkAssets::Load(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+ auto assets = ZipAssetsProvider::Create(path);
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
- bool system) {
- package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U);
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
+// Opens the archive using the file file descriptor with the specified file offset and read length.
+// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
+std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
+ unique_fd fd, const std::string& friendly_name, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
+ const off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+
+ auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
+ return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
+ : nullptr;
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+
+ auto assets = CreateAssetFromFile(path);
+ return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
+ unique_fd fd, const std::string& friendly_name, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
+ const off64_t length) {
+
+ auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
+ return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
+ std::move(override_asset))
+ : nullptr;
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
- bool system) {
+ const package_property_t flags) {
+ CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
if (idmap_asset == nullptr) {
return {};
@@ -76,111 +390,115 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
-
- auto apkPath = loaded_idmap->OverlayApkPath();
- return LoadImpl({} /*fd*/, apkPath,
- std::move(idmap_asset),
- std::move(loaded_idmap),
- PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
+
+ auto overlay_path = loaded_idmap->OverlayApkPath();
+ auto assets = ZipAssetsProvider::Create(overlay_path);
+ return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
+ nullptr /* override_asset */, std::move(idmap_asset),
+ std::move(loaded_idmap))
+ : nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
- const std::string& friendly_name,
- bool system, bool force_shared_lib,
- bool for_loader) {
- package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
- (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
- (for_loader ? PROPERTY_LOADER : 0U);
- return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- flags);
-}
+std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
-std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
- bool for_loader) {
- return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
+ auto assets = DirectoryAssetsProvider::Create(path);
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
+ : nullptr;
}
-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 ? PROPERTY_LOADER : 0U);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
+ const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
+
+ auto assets = (override_asset) ? std::move(override_asset)
+ : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
+ -1 /* last_mod-time */, flags));
+ loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (fd == -1) {
+ if (!fd.ok()) {
LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
return {};
}
- const off64_t file_len = lseek64(fd, 0, SEEK_END);
- if (file_len < 0) {
- LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno);
- return {};
+ return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset,
+ off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+ if (length == kUnknownLength) {
+ length = lseek64(fd, 0, SEEK_END);
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+ << SystemErrorCodeToString(errno);
+ return {};
+ }
}
std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
- if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno);
+ if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
+ << SystemErrorCodeToString(errno);
return {};
}
- return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
+
+ // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+ // Asset::openFileDescriptor can use `path` to create new file descriptors.
+ return Asset::createFromUncompressedMap(std::move(file_map),
+ (path) ? base::unique_fd(-1) : std::move(fd),
+ Asset::AccessMode::ACCESS_RANDOM);
}
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, package_property_t property_flags) {
- ::ZipArchiveHandle unmanaged_handle;
- int32_t result;
- if (fd >= 0) {
- result =
- ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/);
- } else {
- result = ::OpenArchive(path.c_str(), &unmanaged_handle);
- }
+ std::unique_ptr<const AssetsProvider> assets, const std::string& path,
+ package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
+ std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
+ const time_t last_mod_time = getFileModDate(path.c_str());
+
+ // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
+ bool resources_asset_exists = false;
+ auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+ &resources_asset_exists);
- time_t last_mod_time = getFileModDate(path.c_str());
+ assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
// 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, property_flags));
+ loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
- // Find the resource table.
- ::ZipEntry entry;
- result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
- if (result != 0) {
- // There is no resources.arsc, so create an empty LoadedArsc and return.
+ if (!resources_asset_exists) {
loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
return std::move(loaded_apk);
}
- if (entry.method == kCompressDeflated) {
- ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
- }
-
- // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
- loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
- if (loaded_apk->resources_asset_ == nullptr) {
+ loaded_apk->resources_asset_ = std::move(resources_asset_);
+ if (!loaded_apk->resources_asset_) {
LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
loaded_apk->idmap_asset_ = std::move(idmap_asset);
- loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
+ loaded_apk->loaded_idmap_ = std::move(idmap);
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, loaded_apk->loaded_idmap_.get(),
property_flags);
- if (loaded_apk->loaded_arsc_ == nullptr) {
+ if (!loaded_apk->loaded_arsc_) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
@@ -189,27 +507,17 @@ 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,
- package_property_t property_flags) {
- std::unique_ptr<Asset> resources_asset;
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
+ std::unique_ptr<Asset> resources_asset, const std::string& path,
+ package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
- 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 {};
- }
+ const time_t last_mod_time = getFileModDate(path.c_str());
- time_t last_mod_time = getFileModDate(path.c_str());
+ auto assets = (override_assets) ? std::move(override_assets)
+ : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
std::unique_ptr<ApkAssets> loaded_apk(
- new ApkAssets(nullptr, path, last_mod_time, property_flags));
+ new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
loaded_apk->resources_asset_ = std::move(resources_asset);
const StringPiece data(
@@ -225,111 +533,9 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
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 {
- // 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);
- if (result != 0) {
- return {};
- }
-
- if (entry.method == kCompressDeflated) {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
- entry.compressed_length, true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to decompress '" << path << "'.";
- return {};
- }
- return asset;
- } else {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
- entry.uncompressed_length, true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
- return {};
- }
- return asset;
- }
-}
-
-bool ApkAssets::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const {
- // 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() != '/') {
- root_path_full += '/';
- }
-
- void* cookie;
- if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
- return false;
- }
-
- std::string name;
- ::ZipEntry entry;
-
- // We need to hold back directories because many paths will contain them and we want to only
- // surface one.
- std::set<std::string> dirs;
-
- int32_t result;
- while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(name);
- StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
- if (!leaf_file_path.empty()) {
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
- dirs.insert(std::move(dir));
- } else {
- f(leaf_file_path, kFileTypeRegular);
- }
- }
- }
- ::EndIteration(cookie);
-
- // Now present the unique directories.
- for (const std::string& dir : dirs) {
- f(dir, kFileTypeDirectory);
- }
-
- // -1 is end of iteration, anything else is an error.
- return result == -1;
-}
-
bool ApkAssets::IsUpToDate() const {
if (IsLoader()) {
- // Loaders are invalidated by the app, not the system, so assume up to date.
+ // Loaders are invalidated by the app, not the system, so assume they are up to date.
return true;
}
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index c132f343713f..cd30c184d5a4 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -298,14 +298,13 @@ Asset::Asset(void)
/*
* Create a new Asset from a memory mapping.
*/
-/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
- AccessMode mode)
+/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode)
{
_FileAsset* pAsset;
status_t result;
pAsset = new _FileAsset;
- result = pAsset->openChunk(dataMap);
+ result = pAsset->openChunk(dataMap, base::unique_fd(-1));
if (result != NO_ERROR) {
delete pAsset;
return NULL;
@@ -316,11 +315,11 @@ Asset::Asset(void)
}
/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- AccessMode mode)
+ base::unique_fd fd, AccessMode mode)
{
std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
- status_t result = pAsset->openChunk(dataMap.get());
+ status_t result = pAsset->openChunk(dataMap.get(), std::move(fd));
if (result != NO_ERROR) {
return NULL;
}
@@ -415,7 +414,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m
* Constructor.
*/
_FileAsset::_FileAsset(void)
- : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
+ : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -485,7 +484,7 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz
/*
* Create the chunk from the map.
*/
-status_t _FileAsset::openChunk(FileMap* dataMap)
+status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd)
{
assert(mFp == NULL); // no reopen
assert(mMap == NULL);
@@ -494,6 +493,7 @@ status_t _FileAsset::openChunk(FileMap* dataMap)
mMap = dataMap;
mStart = -1; // not used
mLength = dataMap->getDataLength();
+ mFd = std::move(fd);
assert(mOffset == 0);
return NO_ERROR;
@@ -692,6 +692,17 @@ const void* _FileAsset::getBuffer(bool wordAligned)
int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
{
if (mMap != NULL) {
+ if (mFd.ok()) {
+ *outStart = mMap->getDataOffset();
+ *outLength = mMap->getDataLength();
+ const int fd = dup(mFd);
+ if (fd < 0) {
+ ALOGE("Unable to dup fd (%d).", mFd.get());
+ return -1;
+ }
+ lseek64(fd, 0, SEEK_SET);
+ return fd;
+ }
const char* fname = mMap->getFileName();
if (fname == NULL) {
fname = mFileName;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 32086625a726..f20e18453f8b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -463,7 +463,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con
files->add(info);
};
- if (!apk_assets->ForEachFile(full_path, func)) {
+ if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) {
return {};
}
}
@@ -487,7 +487,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
continue;
}
- std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
+ std::unique_ptr<Asset> asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode);
if (asset) {
if (out_cookie != nullptr) {
*out_cookie = i;
@@ -508,7 +508,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
}
- return apk_assets_[cookie]->Open(filename, mode);
+ return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index e35c0249fbdf..70bb441f94cb 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -749,7 +749,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap,
- package_property_t property_flags) {
+ const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index af802b0e50b9..879b050b65bd 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -35,62 +35,98 @@ namespace android {
class LoadedIdmap;
+// Interface for retrieving assets provided by an ApkAssets.
+class AssetsProvider {
+ public:
+ virtual ~AssetsProvider() = default;
+
+ // Opens a file for reading.
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+ bool* file_exists = nullptr) const {
+ return OpenInternal(path, mode, file_exists);
+ }
+
+ // Iterate over all files and directories provided by the zip. The order of iteration is stable.
+ virtual bool ForEachFile(const std::string& /* path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+ }
+
+ protected:
+ AssetsProvider() = default;
+
+ virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
+};
+
+class ZipAssetsProvider;
+
// Holds an APK.
class ApkAssets {
public:
+ // This means the data extends to the end of the file.
+ static constexpr off64_t kUnknownLength = -1;
+
// 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,
- bool for_loader = false);
+ static std::unique_ptr<const ApkAssets> Load(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
- // 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
- // filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path,
- bool system = false);
+ // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
+ // descriptor. The `friendly_name` is some name that will be used to identify the source of
+ // this ApkAssets in log messages and other debug scenarios.
+ // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
+ // using the `offset` into the file descriptor and will be `length` bytes long.
+ static std::unique_ptr<const ApkAssets> LoadFromFd(
+ base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
+ off64_t length = kUnknownLength);
+
+ // Creates an ApkAssets from the given path which points to a resources.arsc.
+ static std::unique_ptr<const ApkAssets> LoadTable(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+
+ // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
+ // takes ownership of the file descriptor.
+ // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
+ // using the `offset` into the file descriptor and will be `length` bytes long.
+ static std::unique_ptr<const ApkAssets> LoadTableFromFd(
+ base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
+ off64_t length = kUnknownLength);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
- // 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> LoadOverlay(const std::string& idmap_path,
- bool system = false);
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
- // descriptor. The `friendly_name` is some name that will be used to identify the source of
- // this ApkAssets in log messages and other debug scenarios.
- // 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.
- // 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 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 for_loader = false);
+ // Creates an ApkAssets from the directory path. File-based resources are read within the
+ // directory as if the directory is an APK.
+ static std::unique_ptr<const ApkAssets> LoadFromDir(
+ const std::string& path, package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
// Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(bool for_loader = false);
-
- std::unique_ptr<Asset> Open(const std::string& path,
- Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
-
- bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const;
+ static std::unique_ptr<const ApkAssets> LoadEmpty(
+ package_property_t flags = 0U,
+ std::unique_ptr<const AssetsProvider> override_asset = nullptr);
inline const std::string& GetPath() const {
return path_;
}
+ inline const AssetsProvider* GetAssetsProvider() const {
+ return assets_provider_.get();
+ }
+
// This is never nullptr.
inline const LoadedArsc* GetLoadedArsc() const {
return loaded_arsc_.get();
@@ -105,34 +141,44 @@ class ApkAssets {
}
inline bool IsOverlay() const {
- return (property_flags_ & PROPERTY_OVERLAY) != 0;
+ return loaded_idmap_ != nullptr;
}
bool IsUpToDate() const;
- // Creates an Asset from any file on the file system.
+ // Creates an Asset from a file on disk.
static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+ // Creates an Asset from a file descriptor.
+ //
+ // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+ // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+ // descriptor and will be `length` bytes long.
+ static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset = 0,
+ off64_t length = kUnknownLength);
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,
- package_property_t property_flags);
+ static std::unique_ptr<const ApkAssets> LoadImpl(
+ std::unique_ptr<const AssetsProvider> assets, const std::string& path,
+ package_property_t property_flags,
+ std::unique_ptr<const AssetsProvider> override_assets = nullptr,
+ std::unique_ptr<Asset> idmap_asset = nullptr,
+ std::unique_ptr<const LoadedIdmap> idmap = nullptr);
- static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
- const std::string& path,
- package_property_t property_flags);
+ static std::unique_ptr<const ApkAssets> LoadTableImpl(
+ std::unique_ptr<Asset> resources_asset, const std::string& path,
+ package_property_t property_flags,
+ std::unique_ptr<const AssetsProvider> override_assets = nullptr);
- ApkAssets(ZipArchiveHandle unmanaged_handle,
- const std::string& path,
+ ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
+ std::string path,
time_t last_mod_time,
package_property_t property_flags);
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
-
- ZipArchivePtr zip_handle_;
+ std::unique_ptr<const AssetsProvider> assets_provider_;
const std::string path_;
time_t last_mod_time_;
package_property_t property_flags_ = 0U;
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 053dbb7864c6..298509eb37a1 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -26,6 +26,7 @@
#include <memory>
+#include <android-base/unique_fd.h>
#include <utils/Compat.h>
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -158,6 +159,7 @@ private:
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
friend class ApkAssets;
+ friend class ZipAssetsProvider;
/*
* Create the asset from a named file on disk.
@@ -202,8 +204,14 @@ private:
*/
static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
+ /*
+ * Create the asset from a memory-mapped file segment.
+ *
+ * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is
+ * used to request new file descriptors using "openFileDescriptor".
+ */
static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- AccessMode mode);
+ base::unique_fd fd, AccessMode mode);
/*
* Create the asset from a memory-mapped file segment with compressed
@@ -256,9 +264,9 @@ public:
/*
* Use a memory-mapped region.
*
- * On success, the object takes ownership of "dataMap".
+ * On success, the object takes ownership of "dataMap" and "fd".
*/
- status_t openChunk(FileMap* dataMap);
+ status_t openChunk(FileMap* dataMap, base::unique_fd fd);
/*
* Standard Asset interfaces.
@@ -273,11 +281,12 @@ public:
virtual bool isAllocated(void) const { return mBuf != NULL; }
private:
- off64_t mStart; // absolute file offset of start of chunk
- off64_t mLength; // length of the chunk
- off64_t mOffset; // current local offset, 0 == mStart
- FILE* mFp; // for read/seek
- char* mFileName; // for opening
+ off64_t mStart; // absolute file offset of start of chunk
+ off64_t mLength; // length of the chunk
+ off64_t mOffset; // current local offset, 0 == mStart
+ FILE* mFp; // for read/seek
+ char* mFileName; // for opening
+ base::unique_fd mFd; // for opening file descriptors
/*
* To support getBuffer() we either need to read the entire thing into
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index b5d3a1fc6c1f..89ff9f52125d 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -69,12 +69,24 @@ struct TypeSpec {
}
};
+// Flags that change the behavior of loaded packages.
+// Keep in sync with f/b/android/content/res/ApkAssets.java
using package_property_t = uint32_t;
enum : package_property_t {
- PROPERTY_DYNAMIC = 1,
- PROPERTY_LOADER = 2,
- PROPERTY_OVERLAY = 4,
- PROPERTY_SYSTEM = 8,
+ // The package contains framework resource values specified by the system.
+ // This allows some functions to filter out this package when computing
+ // what configurations/resources are available.
+ PROPERTY_SYSTEM = 1U << 0U,
+
+ // The package is a shared library or has a package id of 7f and is loaded as a shared library by
+ // force.
+ PROPERTY_DYNAMIC = 1U << 1U,
+
+ // The package has been loaded dynamically using a ResourcesProvider.
+ PROPERTY_LOADER = 1U << 2U,
+
+ // The package is a RRO.
+ PROPERTY_OVERLAY = 1U << 3U,
};
// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 0f2ee6fb968e..19db25ce8811 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -42,7 +42,7 @@ TEST(ApkAssetsTest, LoadApk) {
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkFromFd) {
@@ -50,14 +50,13 @@ TEST(ApkAssetsTest, LoadApkFromFd) {
unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
ASSERT_THAT(fd.get(), Ge(0));
- std::unique_ptr<const ApkAssets> loaded_apk =
- ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
+ std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path);
ASSERT_THAT(loaded_apk, NotNull());
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
@@ -70,7 +69,7 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
- loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+ loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_apk, NotNull());
loaded_arsc = loaded_apk->GetLoadedArsc();
@@ -84,9 +83,11 @@ TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_THAT(loaded_apk, NotNull());
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml",
+ Asset::ACCESS_BUFFER), NotNull()); }
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml",
+ Asset::ACCESS_BUFFER), NotNull()); }
}
TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
@@ -94,7 +95,8 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_THAT(loaded_apk, NotNull());
- auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
+ auto asset = loaded_apk->GetAssetsProvider()->Open("assets/uncompressed.txt",
+ Asset::ACCESS_UNKNOWN);
ASSERT_THAT(asset, NotNull());
off64_t start, length;
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 35fea7ab86cb..ac32699c6dfd 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -63,10 +63,12 @@ class AssetManager2Test : public ::testing::Test {
libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
ASSERT_NE(nullptr, libclient_assets_);
- appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+ appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk",
+ PROPERTY_DYNAMIC);
ASSERT_NE(nullptr, appaslib_assets_);
- system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk",
+ PROPERTY_SYSTEM);
ASSERT_NE(nullptr, system_assets_);
app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index c8dbe205fee2..24361b5817f4 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -67,7 +67,7 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest {
TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
AssetManager2 assetmanager;
- auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk");
+ auto apk_assets = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk", PROPERTY_DYNAMIC);
ASSERT_NE(nullptr, apk_assets);
assetmanager.SetApkAssets({apk_assets.get()});
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index b679672ab34e..41ba637da5d7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -221,8 +221,8 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) {
TEST_F(IdmapTest, OverlayLoaderInterop) {
std::string contents;
- auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc",
- /* for_loader */ true);
+ auto loader_assets = ApkAssets::LoadTable(GetTestDataPath() + "/loader/resources.arsc",
+ PROPERTY_LOADER);
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index be5ecd94a588..16b9c75982fb 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -36,7 +36,7 @@ namespace android {
class ThemeTest : public ::testing::Test {
public:
void SetUp() override {
- system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", PROPERTY_SYSTEM);
ASSERT_NE(nullptr, system_assets_);
style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index 09cdbd5fee58..eaa3e04cc814 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -100,10 +100,12 @@ void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& as
dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))};
std::vector<dex::MethodBuilder> methods;
- assets->ForEachFile("res/", [&](const android::StringPiece& s, android::FileType) {
+ assets->GetAssetsProvider()->ForEachFile("res/", [&](const android::StringPiece& s,
+ android::FileType) {
if (s == "layout") {
auto path = StringPrintf("res/%s/", s.to_string().c_str());
- assets->ForEachFile(path, [&](const android::StringPiece& layout_file, android::FileType) {
+ assets->GetAssetsProvider()->ForEachFile(path, [&](const android::StringPiece& layout_file,
+ android::FileType) {
auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str());
android::ApkAssetsCookie cookie = android::kInvalidCookie;
auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie);
@@ -166,8 +168,7 @@ void CompileApkLayouts(const std::string& filename, CompilationTarget target,
void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
std::ostream& target_out) {
constexpr const char* friendly_name{"viewcompiler assets"};
- auto assets = android::ApkAssets::LoadFromFd(
- std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false);
+ auto assets = android::ApkAssets::LoadFromFd(std::move(fd), friendly_name);
CompileApkAssetsLayouts(assets, target, target_out);
}