summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/app/ResourcesManager.java5
-rw-r--r--core/java/android/content/pm/PackageParser.java3
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java2
-rw-r--r--core/java/android/content/res/ApkAssets.java235
-rw-r--r--core/java/android/content/res/AssetManager.java9
-rw-r--r--core/java/android/content/res/Resources.java9
-rw-r--r--core/java/android/content/res/loader/ResourcesLoader.java22
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java117
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp138
-rw-r--r--libs/androidfw/ApkAssets.cpp230
-rw-r--r--libs/androidfw/Asset.cpp25
-rw-r--r--libs/androidfw/LoadedArsc.cpp2
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h91
-rw-r--r--libs/androidfw/include/androidfw/Asset.h24
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h20
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp2
-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
20 files changed, 600 insertions, 350 deletions
diff --git a/api/current.txt b/api/current.txt
index d944d24d2993..861d6e8e5958 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12871,11 +12871,8 @@ package android.content.res.loader {
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 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;
}
}
@@ -82233,3 +82230,4 @@ package org.xmlpull.v1.sax2 {
}
}
+
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 81671c349cbd..60f61cef0b61 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -368,10 +368,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) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c6875a4b3443..5ade26153296 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
@@ -1442,7 +1441,7 @@ public class PackageParser {
try {
try {
apkAssets = fd != null
- ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
: 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..88b4c290c52a 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 */)
: 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..b9dad85fe84c 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -15,17 +15,19 @@
*/
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.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 +41,72 @@ 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;
+
+ // Format types that change how the apk assets are loaded.
+ @IntDef(prefix = { "FORMAT_" }, value = {
+ FORMAT_APK,
+ FORMAT_IDMAP,
+ FORMAT_ARSC,
+ })
+ @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;
/**
* Creates a new ApkAssets instance from the given path on disk.
@@ -56,59 +116,59 @@ 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);
}
/**
- * Creates a new ApkAssets instance from the given path on disk.
+ * Creates a new ApkAssets instance from the given file descriptor.
*
- * @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.
+ * 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 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,
- boolean forceSharedLibrary) throws IOException {
- return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
- false /*arscOnly*/, false /*forLoader*/);
+ public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, flags);
}
/**
- * 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 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
* @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)
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
throws IOException {
- return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
- false /*forLoader*/);
+ return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags);
}
/**
@@ -116,99 +176,101 @@ public final class ApkAssets {
* is encoded within the IDMAP.
*
* @param idmapPath Path to the IDMAP of an overlay APK.
- * @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 loadOverlayFromPath(@NonNull String idmapPath, boolean system)
- throws IOException {
- return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
- false /*arscOnly*/, false /*forLoader*/);
+ public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
+ @PropertyFlags int flags) throws IOException {
+ return new ApkAssets(FORMAT_IDMAP, idmapPath, flags);
}
/**
- * Creates a new ApkAssets instance from the given path on disk for use with a
- * {@link ResourcesProvider}.
- *
- * @param path The path to an APK on disk.
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadApkForLoader(@NonNull String path)
- throws IOException {
- return new ApkAssets(path, false /*system*/, false /*forceSharedLibrary*/,
- false /*overlay*/, false /*arscOnly*/, true /*forLoader*/);
- }
-
- /**
- * Creates a new ApkAssets instance from the given file descriptor for use with a
- * {@link ResourcesProvider}.
+ * 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
* @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) throws IOException {
+ return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags);
}
/**
- * 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
* @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)
+ public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
throws IOException {
- return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
- false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
+ return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags);
}
/**
* 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
*/
@NonNull
- public static ApkAssets loadEmptyForLoader() {
- return new ApkAssets(true);
+ public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags) {
+ return new ApkAssets(flags);
}
- private ApkAssets(boolean forLoader) {
- mForLoader = forLoader;
- mNativePtr = nativeLoadEmpty(forLoader);
- mStringBlock = null;
+ private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags)
+ throws IOException {
+ Objects.requireNonNull(path, "path");
+ mFlags = flags;
+ mNativePtr = nativeLoad(format, path, flags);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
- 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) throws IOException {
+ Objects.requireNonNull(fd, "fd");
+ Objects.requireNonNull(friendlyName, "friendlyName");
+ mFlags = flags;
+ mNativePtr = nativeLoadFd(format, fd, friendlyName, flags);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
- 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)
+ 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);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
+ private ApkAssets(@PropertyFlags int flags) {
+ mFlags = flags;
+ mNativePtr = nativeLoadEmpty(flags);
+ mStringBlock = null;
+ }
+
@UnsupportedAppUsage
public @NonNull String getAssetPath() {
synchronized (this) {
@@ -226,8 +288,9 @@ 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;
}
/**
@@ -300,18 +363,14 @@ 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) throws IOException;
+ private static native long nativeLoadEmpty(@PropertyFlags int flags);
+ private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
+ @NonNull String friendlyName, @PropertyFlags int flags) 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;
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..6b9613d6e3be 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -242,12 +242,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);
@@ -443,9 +443,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;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index cb809da3b867..e77d8af49873 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2388,6 +2388,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 +2422,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 +2454,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/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..1d24dda8a4bf 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";
@@ -51,102 +53,142 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
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.
+ *
+ * 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 sharedMemory the shared memory containing the data of the resources table to load
+ * @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,7 +208,8 @@ 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);
}
private ResourcesProvider(@NonNull ApkAssets apkAssets,
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 491d4a61cee4..44f8a31a4e22 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -37,8 +37,21 @@ 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) {
+// 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,
+};
+
+static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
+ jstring java_path, const jint property_flags) {
ScopedUtfChars path(env, java_path);
if (path.c_str() == nullptr) {
return 0;
@@ -47,25 +60,33 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
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);
+ 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);
+ 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) {
ScopedUtfChars friendly_name_utf8(env, friendly_name);
if (friendly_name_utf8.c_str() == nullptr) {
return 0;
@@ -85,49 +106,53 @@ 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);
+ 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);
+ break;
+ case FORMAT_ARSC:
+ apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
+ property_flags);
+ 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) {
+ 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);
-
- 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 +164,37 @@ 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);
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
+
+ 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),
+ 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),
+ 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) {
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadEmpty(flags);
return reinterpret_cast<jlong>(apk_assets.release());
}
@@ -252,13 +296,11 @@ 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;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},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index b2b0ec2a54f8..946fcc03f57e 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -41,28 +41,85 @@ using base::unique_fd;
static const std::string kResourcesArsc("resources.arsc");
ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
- const std::string& path,
+ 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),
+ : zip_handle_(unmanaged_handle, ::CloseArchive),
+ 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);
+std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path,
+ const package_property_t flags) {
+ ::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 LoadImpl(unmanaged_handle, path, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
+ flags);
}
-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);
+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) {
+ 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;
+
+ ::ZipArchiveHandle unmanaged_handle;
+ const int32_t result = (length == 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 LoadImpl(unmanaged_handle, friendly_name, nullptr /*idmap_asset*/,
+ nullptr /*loaded_idmap*/, flags);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(const std::string& path,
+ const package_property_t flags) {
+ auto resources_asset = CreateAssetFromFile(path);
+ if (!resources_asset) {
+ LOG(ERROR) << "Failed to open ARSC '" << path;
+ return {};
+ }
+
+ return LoadTableImpl(std::move(resources_asset), path, flags);
+}
+
+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);
+ if (!resources_asset) {
+ LOG(ERROR) << "Failed to open ARSC '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << length;
+ return {};
+ }
+
+ return LoadTableImpl(std::move(resources_asset), friendly_name, flags);
}
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 {};
@@ -77,75 +134,74 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
return {};
}
- auto apkPath = loaded_idmap->OverlayApkPath();
- return LoadImpl({} /*fd*/, apkPath,
- std::move(idmap_asset),
- std::move(loaded_idmap),
- PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
-}
-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);
-}
+ ::ZipArchiveHandle unmanaged_handle;
+ auto overlay_path = loaded_idmap->OverlayApkPath();
+ const int32_t result = ::OpenArchive(overlay_path.c_str(), &unmanaged_handle);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open overlay APK '" << overlay_path << "' "
+ << ::ErrorCodeString(result);
+ ::CloseArchive(unmanaged_handle);
+ return {};
+ }
-std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
- bool for_loader) {
- return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
+ return LoadImpl(unmanaged_handle, overlay_path, std::move(idmap_asset), std::move(loaded_idmap),
+ flags | PROPERTY_OVERLAY);
}
-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<ApkAssets> loaded_apk(new ApkAssets(nullptr, "empty", -1, 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 {};
- }
-
- 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);
- return {};
- }
- return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
+ return CreateAssetFromFd(std::move(fd), path.c_str());
}
-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<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 {};
+ }
}
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
+ std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
+ 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 {};
}
- time_t last_mod_time = getFileModDate(path.c_str());
+ // 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(ZipArchiveHandle unmanaged_handle,
+ const std::string& path,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<const LoadedIdmap> idmap,
+ package_property_t property_flags) {
+ const time_t last_mod_time = getFileModDate(path.c_str());
// Wrap the handle in a unique_ptr so it gets automatically closed.
std::unique_ptr<ApkAssets>
@@ -153,7 +209,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
// Find the resource table.
::ZipEntry entry;
- result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
+ int32_t result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry);
if (result != 0) {
// There is no resources.arsc, so create an empty LoadedArsc and return.
loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
@@ -173,7 +229,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
// 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*/)),
@@ -189,24 +245,10 @@ 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;
-
- if (fd >= 0) {
- resources_asset = std::unique_ptr<Asset>(Asset::createFromFd(fd.release(), nullptr,
- Asset::AccessMode::ACCESS_BUFFER));
- } else {
- resources_asset = CreateAssetFromFile(path);
- }
-
- if (resources_asset == nullptr) {
- LOG(ERROR) << "Failed to open ARSC '" << path;
- return {};
- }
-
- time_t last_mod_time = getFileModDate(path.c_str());
+std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(std::unique_ptr<Asset> resources_asset,
+ const std::string& path,
+ package_property_t property_flags) {
+ const time_t last_mod_time = getFileModDate(path.c_str());
std::unique_ptr<ApkAssets> loaded_apk(
new ApkAssets(nullptr, path, last_mod_time, property_flags));
@@ -225,13 +267,6 @@ 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) {
@@ -244,10 +279,12 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod
return {};
}
+ 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(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
- entry.compressed_length, true /*readOnly*/)) {
+ if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.compressed_length,
+ true /*readOnly*/)) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
return {};
}
@@ -261,13 +298,18 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod
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*/)) {
+ if (!map->create(path_.c_str(), fd, fd_offset + 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);
+ // TODO: apks created from file descriptors residing in RAM currently cannot open file
+ // descriptors to the assets they contain. This is because the Asset::openFileDeescriptor uses
+ // the zip path on disk to create a new file descriptor. This is fixed in a future change
+ // in the change topic.
+ std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
+ unique_fd(-1) /* fd*/, mode);
if (asset == nullptr) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
return {};
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/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..643dc5c861f7 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -37,49 +37,46 @@ class LoadedIdmap;
// Holds an APK.
class ApkAssets {
- public:
- // 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);
-
- // 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);
+ // This means the data extends to the end of the file.
+ static constexpr off64_t kUnknownLength = -1;
- // 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);
+ public:
+ // Creates an ApkAssets from the zip path.
+ static std::unique_ptr<const ApkAssets> Load(const std::string& path,
+ 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.
+ // 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, bool system,
- bool force_shared_lib,
- bool for_loader = false);
+ const std::string& friendly_name,
+ package_property_t flags = 0U,
+ 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);
+
+ // 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);
- // 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 an IDMAP, which contains the original APK path, and the overlay
+ // data.
+ static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
+ package_property_t flags = 0U);
// Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(bool for_loader = false);
+ static std::unique_ptr<const ApkAssets> LoadEmpty(package_property_t flags = 0U);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -105,28 +102,38 @@ 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,
+ static std::unique_ptr<const ApkAssets> LoadImpl(ZipArchiveHandle unmanaged_handle,
+ const std::string& path,
std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap,
+ std::unique_ptr<const LoadedIdmap> idmap,
package_property_t property_flags);
- 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);
ApkAssets(ZipArchiveHandle unmanaged_handle,
- const std::string& path,
+ std::string path,
time_t last_mod_time,
package_property_t property_flags);
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 053dbb7864c6..75761747a5b4 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>
@@ -202,8 +203,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 +263,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 +280,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..26bf5ffe5e91 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -70,7 +70,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();
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(),