summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Mitchell <rtmitchell@google.com>2020-03-11 13:15:28 -0700
committerRyan Mitchell <rtmitchell@google.com>2020-03-19 18:33:55 -0700
commit4ea1e4288985508e3e0f21febe4da242c86a7dd1 (patch)
treee66c14e354dab6b573f4ca8dd643642e35ef54d2
parentc07aa702703388747bd6e9b1091127e2736ffcd8 (diff)
Move AssetsProvider to native layer
Querying in the native layer for assets provided through AssetsProviders does not currently work. This change refactors the AssetProvider API to return a file descriptor that is read in the native layer and can bubble up to the java layer. This change also removes the InputStream API to favor of developers using memfd_create. Bug: 142716192 Test: atest ResourceLoaderValuesTest Change-Id: I1a7eca0994c3b7cc32008d9a72bf91086ff0e816
-rw-r--r--api/current.txt10
-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.java101
-rw-r--r--core/java/android/content/res/AssetManager.java155
-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/ResourcesProvider.java35
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp167
-rw-r--r--core/jni/android_util_AssetManager.cpp12
-rw-r--r--core/tests/ResourceLoaderTests/assets/base_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider1/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider2/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider3/assets/loader_asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/resources/provider4/assets/loader_asset.txt1
-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.kt218
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt190
-rw-r--r--libs/androidfw/ApkAssets.cpp157
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h57
-rw-r--r--startop/view_compiler/apk_layout_compiler.cc3
21 files changed, 672 insertions, 762 deletions
diff --git a/api/current.txt b/api/current.txt
index 5ae65901fec8..8a35cc779ceb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12846,14 +12846,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 {
@@ -12868,7 +12861,6 @@ 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 loadFromDirectory(@NonNull String, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5ade26153296..24589cfec27a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1441,7 +1441,7 @@ public class PackageParser {
try {
try {
apkAssets = fd != null
- ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
+ ? 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 88b4c290c52a..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, 0 /* flags */)
+ ? 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 078d175b9a80..bc418061e1d1 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -20,6 +20,7 @@ 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 com.android.internal.annotations.GuardedBy;
@@ -112,6 +113,9 @@ public final class ApkAssets {
@PropertyFlags
private final int mFlags;
+ @Nullable
+ private final AssetsProvider mAssets;
+
/**
* Creates a new ApkAssets instance from the given path on disk.
*
@@ -133,7 +137,21 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
throws IOException {
- return new ApkAssets(FORMAT_APK, path, flags);
+ 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 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, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_APK, path, flags, assets);
}
/**
@@ -145,12 +163,14 @@ public final class ApkAssets {
* @param fd The FileDescriptor of an open, readable APK.
* @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.
*/
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
- return new ApkAssets(FORMAT_APK, fd, friendlyName, flags);
+ @NonNull String friendlyName, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
}
/**
@@ -166,13 +186,15 @@ public final class ApkAssets {
* @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 loadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets)
throws IOException {
- return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags);
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
}
/**
@@ -186,7 +208,7 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
@PropertyFlags int flags) throws IOException {
- return new ApkAssets(FORMAT_IDMAP, idmapPath, flags);
+ return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
}
/**
@@ -199,12 +221,14 @@ public final class ApkAssets {
* @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.
*/
public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
- return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags);
+ @NonNull String friendlyName, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
}
/**
@@ -221,13 +245,14 @@ public final class ApkAssets {
* @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)
- throws IOException {
- return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags);
+ @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);
}
/**
@@ -236,12 +261,13 @@ public final class ApkAssets {
*
* @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 loadFromDir(@NonNull String path,
- @PropertyFlags int flags) throws IOException {
- return new ApkAssets(FORMAT_DIR, path, flags);
+ @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
+ return new ApkAssets(FORMAT_DIR, path, flags, assets);
}
/**
@@ -250,43 +276,50 @@ public final class ApkAssets {
* 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(@PropertyFlags int flags) {
- return new ApkAssets(flags);
+ public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
+ @Nullable AssetsProvider assets) {
+ return new ApkAssets(flags, assets);
}
- private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags)
- throws IOException {
+ 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);
+ mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
- @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
+ @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);
+ mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
- @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
- throws IOException {
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) throws IOException {
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
mFlags = flags;
- mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags);
+ mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ mAssets = assets;
}
- private ApkAssets(@PropertyFlags int flags) {
+ private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
mFlags = flags;
- mNativePtr = nativeLoadEmpty(flags);
+ mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
+ mAssets = assets;
}
@UnsupportedAppUsage
@@ -312,6 +345,14 @@ public final class ApkAssets {
}
/**
+ * Returns the assets provider that overrides the loading of assets present in this apk assets.
+ */
+ @Nullable
+ public AssetsProvider getAssetsProvider() {
+ return mAssets;
+ }
+
+ /**
* Retrieve a parser for a compiled XML file. This is associated with a single APK and
* <em>NOT</em> a full AssetManager. This means that shared-library references will not be
* dynamically assigned runtime package IDs.
@@ -382,13 +423,15 @@ public final class ApkAssets {
}
private static native long nativeLoad(@FormatType int format, @NonNull String path,
- @PropertyFlags int flags) throws IOException;
- private static native long nativeLoadEmpty(@PropertyFlags int flags);
+ @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) throws IOException;
+ @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) throws IOException;
+ @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 6b9613d6e3be..7b2b93949a9f 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;
@@ -828,13 +825,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);
@@ -859,13 +849,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);
@@ -959,12 +942,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);
@@ -1004,12 +981,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) {
@@ -1072,15 +1043,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);
}
@@ -1090,122 +1053,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/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/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
index 040b3694d0cd..0a698d18682b 100644
--- a/core/java/android/content/res/loader/ResourcesProvider.java
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -50,8 +50,6 @@ 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
* file-based assets not associated with resource identifiers.
@@ -60,8 +58,8 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
*/
@NonNull
public static ResourcesProvider empty(@NonNull AssetsProvider assetsProvider) {
- return new ResourcesProvider(ApkAssets.loadEmptyForLoader(ApkAssets.PROPERTY_LOADER),
- assetsProvider);
+ return new ResourcesProvider(ApkAssets.loadEmptyForLoader(ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
@@ -101,7 +99,7 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
@Nullable AssetsProvider assetsProvider)
throws IOException {
return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
- fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER), assetsProvider);
+ fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
}
/**
@@ -130,8 +128,8 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
long offset, long length, @Nullable AssetsProvider assetsProvider)
throws IOException {
return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
- fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER),
- assetsProvider);
+ fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
@@ -156,7 +154,7 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
throws IOException {
return new ResourcesProvider(
ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
- fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER), assetsProvider);
+ fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER, assetsProvider));
}
/**
@@ -187,8 +185,8 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
throws IOException {
return new ResourcesProvider(
ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
- fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER),
- assetsProvider);
+ fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
/**
@@ -208,8 +206,8 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
}
String splitPath = appInfo.getSplitCodePaths()[splitIndex];
- return new ResourcesProvider(ApkAssets.loadFromPath(splitPath, ApkAssets.PROPERTY_LOADER),
- null);
+ return new ResourcesProvider(ApkAssets.loadFromPath(splitPath, ApkAssets.PROPERTY_LOADER,
+ null /* assetsProvider */));
}
/**
@@ -223,20 +221,13 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
@NonNull
public static ResourcesProvider loadFromDirectory(@NonNull String path,
@Nullable AssetsProvider assetsProvider) throws IOException {
- return new ResourcesProvider(ApkAssets.loadFromDir(path, ApkAssets.PROPERTY_LOADER),
- assetsProvider);
+ return new ResourcesProvider(ApkAssets.loadFromDir(path, ApkAssets.PROPERTY_LOADER,
+ assetsProvider));
}
- private ResourcesProvider(@NonNull ApkAssets apkAssets,
- @Nullable AssetsProvider assetsProvider) {
+ private ResourcesProvider(@NonNull ApkAssets apkAssets) {
this.mApkAssets = apkAssets;
- this.mAssetsProvider = assetsProvider;
- }
-
- @Nullable
- public AssetsProvider getAssetsProvider() {
- return mAssetsProvider;
}
/** @hide */
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 6acb133a4e5b..fbdd4060d7f2 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -37,6 +37,21 @@ static struct overlayableinfo_offsets_t {
jmethodID constructor;
} gOverlayableInfoOffsets;
+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 {
@@ -53,8 +68,97 @@ enum : format_type_t {
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) {
+ jstring java_path, const jint property_flags, jobject assets_provider) {
ScopedUtfChars path(env, java_path);
if (path.c_str() == nullptr) {
return 0;
@@ -62,19 +166,20 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma
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;
switch (format) {
case FORMAT_APK:
- apk_assets = ApkAssets::Load(path.c_str(), property_flags);
+ 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);
+ 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);
+ 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);
@@ -92,7 +197,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma
static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
jobject file_descriptor, jstring friendly_name,
- const jint property_flags) {
+ const jint property_flags, jobject assets_provider) {
ScopedUtfChars friendly_name_utf8(env, friendly_name);
if (friendly_name_utf8.c_str() == nullptr) {
return 0;
@@ -112,15 +217,16 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t
return 0;
}
+ 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);
+ 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);
+ property_flags, std::move(loader_assets));
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -140,12 +246,14 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t
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) {
+ 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("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
+
if (offset < 0) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"offset cannot be negative");
@@ -170,18 +278,19 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_
return 0;
}
- ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
-
+ 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, static_cast<off64_t>(offset),
+ 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, static_cast<off64_t>(offset),
+ property_flags, std::move(loader_assets),
+ static_cast<off64_t>(offset),
static_cast<off64_t>(length));
break;
default:
@@ -199,8 +308,9 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_
return reinterpret_cast<jlong>(apk_assets.release());
}
-static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags) {
- std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadEmpty(flags);
+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());
}
@@ -302,11 +412,15 @@ static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong pt
// JNI registration.
static const JNINativeMethod gApkAssetsMethods[] = {
- {"nativeLoad", "(ILjava/lang/String;I)J", (void*)NativeLoad},
- {"nativeLoadEmpty", "(I)J", (void*)NativeLoadEmpty},
- {"nativeLoadFd", "(ILjava/io/FileDescriptor;Ljava/lang/String;I)J", (void*)NativeLoadFromFd},
- {"nativeLoadFdOffsets", "(ILjava/io/FileDescriptor;Ljava/lang/String;JJI)J",
- (void*)NativeLoadFromFdOffset},
+ {"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},
@@ -323,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 062b886f54a1..cb5a332c6e85 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -75,12 +75,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;
@@ -1596,12 +1590,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/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/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/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/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/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/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 bd2bac50f100..4764c1008d2f 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
@@ -20,15 +20,19 @@ import android.content.Context
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 java.io.Closeable
import java.io.FileOutputStream
import java.io.File
+import java.io.FileDescriptor
import java.util.zip.ZipInputStream
abstract class ResourceLoaderTestBase {
@@ -36,6 +40,29 @@ abstract class ResourceLoaderTestBase {
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
@@ -65,86 +92,140 @@ abstract class ResourceLoaderTestBase {
}
}
- protected fun String.openProvider(dataType: DataType)
- :ResourcesProvider = when (dataType) {
- DataType.APK_DISK_FD -> {
- val file = context.copiedAssetFile("${this}.apk")
- ResourcesProvider.loadFromApk(ParcelFileDescriptor.fromFd(file.fd)).apply {
- file.close()
- }
+ protected fun String.openProvider(dataType: DataType, assetsProvider: MemoryAssetsProvider?)
+ :ResourcesProvider {
+ if (assetsProvider != null) {
+ openedObjects += assetsProvider
}
- DataType.APK_DISK_FD_OFFSETS -> {
- val asset = context.assets.openFd("${this}.apk")
- ResourcesProvider.loadFromApk(asset.parcelFileDescriptor, asset.startOffset,
- asset.length, null).apply {
- asset.close()
+ return when (dataType) {
+ DataType.APK_DISK_FD -> {
+ val file = context.copiedAssetFile("${this}.apk")
+ ResourcesProvider.loadFromApk(ParcelFileDescriptor.fromFd(file.fd),
+ assetsProvider).apply {
+ file.close()
+ }
}
- }
- DataType.ARSC_DISK_FD -> {
- val file = context.copiedAssetFile("${this}.arsc")
- ResourcesProvider.loadFromTable(ParcelFileDescriptor.fromFd(file.fd), null).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_OFFSETS -> {
- val asset = context.assets.openFd("${this}.arsc")
- ResourcesProvider.loadFromTable(asset.parcelFileDescriptor, asset.startOffset,
- asset.length, null).apply {
- asset.close()
+ DataType.ARSC_DISK_FD -> {
+ val file = context.copiedAssetFile("${this}.arsc")
+ ResourcesProvider.loadFromTable(ParcelFileDescriptor.fromFd(file.fd),
+ assetsProvider).apply {
+ file.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,
- null).apply {
- asset.close()
- fd.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_FD -> {
- val asset = context.assets.openFd("${this}.apk")
- var fd = loadAssetIntoMemory(asset)
- ResourcesProvider.loadFromApk(fd).apply {
- asset.close()
- fd.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.ARSC_RAM_MEMORY -> {
- val asset = context.assets.openFd("${this}.arsc")
- var fd = loadAssetIntoMemory(asset)
- ResourcesProvider.loadFromTable(fd, null).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_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,
- null).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.SPLIT -> {
- ResourcesProvider.loadFromSplit(context, "${this}_Split")
- }
- DataType.DIRECTORY -> {
- ResourcesProvider.loadFromDirectory(zipToDir("${this}.apk").absolutePath, null)
+ }
+
+ class EmptyAssetsProvider : AssetsProvider
+
+ /** */
+ 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
}
}
+ /** 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
+ }
+
+ override fun loadAssetFd(path: String, accessMode: Int): AssetFileDescriptor? {
+ return if (loadAssetResults.containsKey(path)) AssetFileDescriptor(
+ ParcelFileDescriptor.dup(loadAssetResults[path]), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH) else null
+ }
+
+ override fun close() {
+ for (f in loadAssetResults.values) {
+ Os.close(f)
+ }
+ }
+ }
/** Extracts an archive-based asset into a directory on disk. */
- private fun zipToDir(name : String, suffix : String = "") : File {
- val root = File(context.filesDir, name.split('.')[0] + suffix)
+ private fun zipToDir(name : String) : File {
+ val root = File(context.filesDir, name.split('.')[0])
if (root.exists()) {
return root
}
@@ -210,7 +291,8 @@ abstract class ResourceLoaderTestBase {
ARSC_DISK_FD_OFFSETS,
ARSC_RAM_MEMORY,
ARSC_RAM_MEMORY_OFFSETS,
- SPLIT,
- DIRECTORY
+ 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 c01db0d7428b..a9945369d1df 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
@@ -26,9 +26,7 @@ import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.os.IBinder
-import android.util.ArrayMap
import androidx.test.rule.ActivityTestRule
-import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Rule
@@ -51,26 +49,6 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
private val mTestActivityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
companion object {
- /** Converts the map to a stable JSON string representation. */
- private 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)
- }
- }
-
@Parameterized.Parameters(name = "{1} {0}")
@JvmStatic
fun parameters(): Array<Any> {
@@ -109,21 +87,13 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
// Test resolution of file-based resources and assets with no assets provider.
parameters += Parameter(
- "fileBased",
+ "tableFileBased",
query(mapOf(
// Drawable xml in res directory
"drawableXml" to { res ->
(res.getDrawable(R.drawable.drawable_xml) as ColorDrawable)
.color.toString()
},
- // 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.getLayout(R.layout.layout).advanceToRoot().name
@@ -135,38 +105,109 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
}
)),
mapOf("drawableXml" to Color.parseColor("#B2D2F2").toString(),
- "openAsset" to "In assets directory",
- "openAssetFd" to "In assets directory",
"layout" to "MysteryLayout",
"drawablePng" to Color.parseColor("#FF00FF").toString()),
mapOf("drawableXml" to Color.parseColor("#000001").toString(),
- "openAsset" to "One",
- "openAssetFd" to "One",
"layout" to "RelativeLayout",
"drawablePng" to Color.RED.toString()),
mapOf("drawableXml" to Color.parseColor("#000002").toString(),
- "openAsset" to "Two",
- "openAssetFd" to "Two",
"layout" to "LinearLayout",
"drawablePng" to Color.GREEN.toString()),
mapOf("drawableXml" to Color.parseColor("#000003").toString(),
- "openAsset" to "Three",
- "openAssetFd" to "Three",
"layout" to "FrameLayout",
"drawablePng" to Color.BLUE.toString()),
mapOf("drawableXml" to Color.parseColor("#000004").toString(),
- "openAsset" to "Four",
- "openAssetFd" to "Four",
"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)
)
+ // Test resolution of assets.
+ parameters += Parameter(
+ "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)
+ )
+
+ // Test assets from apk and provider
+ parameters += Parameter(
+ "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)
+
+ )
+
+ // 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 ->
arrayOf(dataType, parameter)
@@ -188,10 +229,15 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
private val valueThree by lazy { mapToString(parameter.valueThree) }
private val valueFour by lazy { mapToString(parameter.valueFour) }
- private fun openOne() = PROVIDER_ONE.openProvider(dataType)
- private fun openTwo() = PROVIDER_TWO.openProvider(dataType)
- private fun openThree() = PROVIDER_THREE.openProvider(dataType)
- private fun openFour() = PROVIDER_FOUR.openProvider(dataType)
+ 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)
@@ -289,6 +335,27 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
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)
fun getProvidersDoesNotLeakMutability() {
val testOne = openOne()
@@ -476,6 +543,9 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
loader1.removeProvider(testOne)
assertEquals(valueFour, getValue())
+
+ loader2.removeProvider(testFour)
+ assertEquals(valueThree, getValue())
}
private fun createContext(context: Context, id: Int): Context {
@@ -644,15 +714,29 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
}
data class Parameter(
- val testPrefix: String,
- val getValue: Resources.() -> String,
- val valueOriginal: Map<String, String>,
- val valueOne: Map<String, String>,
- val valueTwo: Map<String, String>,
- val valueThree: Map<String, String>,
- val valueFour: Map<String, String>,
- val dataTypes: List<DataType>
+ 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>
) {
+ 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/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index f5bf84f18a89..202651dc86d5 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -144,8 +144,8 @@ class ZipAssetsProvider : public AssetsProvider {
}
protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
if (file_exists) {
*file_exists = false;
}
@@ -292,49 +292,91 @@ class EmptyAssetsProvider : public AssetsProvider {
DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
};
-std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path,
- const package_property_t flags) {
+// 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, nullptr /*idmap_asset*/,
- nullptr /*loaded_idmap*/, flags)
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
: nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
- const std::string& friendly_name,
- const package_property_t flags,
- const off64_t offset,
- const off64_t length) {
+// 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, nullptr /*idmap_asset*/,
- nullptr /*loaded_idmap*/, flags)
+ 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) {
- auto resources_asset = CreateAssetFromFile(path);
- return (resources_asset) ? LoadTableImpl(std::move(resources_asset), path, flags)
- : 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,
- const off64_t offset,
- const off64_t length) {
- auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
- return (resources_asset) ? LoadTableImpl(std::move(resources_asset), friendly_name, flags)
- : 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,
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 {};
@@ -351,23 +393,28 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
auto overlay_path = loaded_idmap->OverlayApkPath();
auto assets = ZipAssetsProvider::Create(overlay_path);
- return (assets) ? LoadImpl(std::move(assets), overlay_path, std::move(idmap_asset),
- std::move(loaded_idmap), flags | PROPERTY_OVERLAY)
+ 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::LoadFromDir(const std::string& path,
- const package_property_t flags) {
+std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
+ const std::string& path, const package_property_t flags,
+ std::unique_ptr<const AssetsProvider> override_asset) {
+
auto assets = DirectoryAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/,
- nullptr /*loaded_idmap*/, flags)
+ return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
: nullptr;
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(const package_property_t flags) {
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(
- std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()), "empty" /* path */,
- -1 /* last_mod-time */, flags));
+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);
@@ -413,27 +460,30 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
Asset::AccessMode::ACCESS_RANDOM);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<const AssetsProvider> assets,
- const std::string& path,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> idmap,
- package_property_t property_flags) {
+std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
+ 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) {
+
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);
+
+ 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(std::move(assets), path, last_mod_time, property_flags));
- // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
- bool resources_asset_exists = false;
- loaded_apk->resources_asset_ = loaded_apk->assets_provider_->Open(
- kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, &resources_asset_exists);
-
if (!resources_asset_exists) {
loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
return std::move(loaded_apk);
}
+ loaded_apk->resources_asset_ = std::move(resources_asset_);
if (!loaded_apk->resources_asset_) {
LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -457,14 +507,17 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<const Asset
return std::move(loaded_apk);
}
-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 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) {
+
const 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(std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()), 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(
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 944476890af0..879b050b65bd 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -75,33 +75,33 @@ class ApkAssets {
// Creates an ApkAssets.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(const std::string& path,
- package_property_t flags = 0U);
+ 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 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,
- off64_t offset = 0,
- off64_t length = kUnknownLength);
+ 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);
+ 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,
- off64_t offset = 0,
- off64_t length = kUnknownLength);
+ 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.
@@ -110,11 +110,14 @@ class ApkAssets {
// 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);
+ 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(package_property_t flags = 0U);
+ 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_;
@@ -158,15 +161,17 @@ class ApkAssets {
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
- static std::unique_ptr<const ApkAssets> LoadImpl(std::unique_ptr<const AssetsProvider> assets,
- const std::string& path,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> idmap,
- 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);
+ 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> 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(std::unique_ptr<const AssetsProvider> assets_provider,
std::string path,
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index e70c68852b67..eaa3e04cc814 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -168,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);
}