Refactor ApkAsset loading APIs

To add the partner requested ResourcesProvider#loadFromDir APIs, this
change adds format type integer that allows us to reduce the number of
ApkAssets loading overrides.

This change also adds hidden offset and length based ResourcesProvider
APIs that could not make R.

Bug: 142716192
Test: atest FrameworksResourceLoaderTests
Change-Id: I926fde257cae701901dcd4ca408024feae8c90a6
Merged-In: I926fde257cae701901dcd4ca408024feae8c90a6
diff --git a/api/current.txt b/api/current.txt
index d944d24..861d6e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12871,11 +12871,8 @@
     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 @@
   }
 
 }
+
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 81671c3..60f61ce 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -368,10 +368,9 @@
 
         // 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 c6875a4..5ade261 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,6 @@
 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 @@
         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 4c6da03..88b4c29 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -220,7 +220,7 @@
         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 8db2785..b9dad85 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 @@
  * @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 @@
      * @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.
-     *
-     * @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.
-     * @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*/);
-    }
-
-    /**
-     * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
+     * Creates a new ApkAssets instance from the given file descriptor.
      *
      * Performs a dup of the underlying fd, so you must take care of still closing
      * the FileDescriptor yourself (and can do that whenever you want).
      *
      * @param fd The FileDescriptor of an open, readable APK.
      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
-     * @param system When true, the APK is loaded as a system APK (framework).
-     * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
-     *                           loaded as a shared library.
+     * @param flags flags that change the behavior of loaded apk assets
      * @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, @PropertyFlags int flags) throws IOException {
+        return new ApkAssets(FORMAT_APK, fd, friendlyName, flags);
+    }
+
+    /**
+     * 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 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, 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 @@
      * 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}.
-     *
-     * Performs a dup of the underlying fd, so you must take care of still closing
-     * the FileDescriptor yourself (and can do that whenever you want).
-     *
-     * @param fd The FileDescriptor of an open, readable APK.
-     * @return a new instance of ApkAssets.
-     * @throws IOException if a disk I/O error or parsing error occurred.
-     */
-    @NonNull
-    public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
-        return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
-                false /*system*/, false /*forceSharedLib*/, false /*arscOnly*/, true /*forLoader*/);
-    }
-
-    /**
-     * 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 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, @PropertyFlags int flags) throws IOException {
+        return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags);
+    }
+
+    /**
+     * 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 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 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(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
-            boolean arscOnly, boolean forLoader) throws IOException {
-        mForLoader = forLoader;
+    private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags)
+            throws IOException {
         Objects.requireNonNull(path, "path");
-        mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
-                : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
+        mFlags = flags;
+        mNativePtr = nativeLoad(format, path, 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, @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 = nativeLoadFd(format, fd, friendlyName, flags);
         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
     }
 
+    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");
+        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 @@
         }
     }
 
+    /** 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 @@
         }
     }
 
-    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 f295f8c..6b9613d 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -242,12 +242,12 @@
 
         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 @@
                     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 cb809da..e77d8af 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2388,6 +2388,9 @@
      * 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 @@
      * 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 @@
 
     /**
      * 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 58fec60..c308400 100644
--- a/core/java/android/content/res/loader/ResourcesLoader.java
+++ b/core/java/android/content/res/loader/ResourcesLoader.java
@@ -45,6 +45,11 @@
  *
  * <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 @@
      * 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 @@
      * 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 @@
     /**
      * 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 @@
         }
     }
 
-    /** 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 @@
         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 419ec78..1d24dda 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.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,70 +53,110 @@
     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
+     * <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 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)
+    public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor,
+            long offset, long length, @Nullable AssetsProvider assetsProvider)
             throws IOException {
-        return loadFromApk(sharedMemory, null);
+        return new ResourcesProvider(ApkAssets.loadFromFd(fileDescriptor.getFileDescriptor(),
+                fileDescriptor.toString(), offset, length, ApkAssets.PROPERTY_LOADER),
+                assetsProvider);
     }
 
     /**
-     * Creates a ResourcesProvider from an {@code .apk} file representation in memory.
+     * Creates a ResourcesProvider from a resources table ({@code .arsc}) file descriptor.
      *
-     * @param sharedMemory the shared memory containing the data of the APK to load
+     * <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 loadFromApk(@NonNull SharedMemory sharedMemory,
+    public static ResourcesProvider loadFromTable(@NonNull ParcelFileDescriptor fileDescriptor,
             @Nullable AssetsProvider assetsProvider)
             throws IOException {
         return new ResourcesProvider(
-                ApkAssets.loadApkForLoader(sharedMemory.getFileDescriptor()), assetsProvider);
+                ApkAssets.loadTableFromFd(fileDescriptor.getFileDescriptor(),
+                        fileDescriptor.toString(), ApkAssets.PROPERTY_LOADER), assetsProvider);
     }
 
     /**
@@ -123,30 +165,30 @@
      * 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
+     * @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 ParcelFileDescriptor fileDescriptor,
-            @Nullable AssetsProvider assetsProvider)
+            long offset, long length, @Nullable AssetsProvider assetsProvider)
             throws IOException {
         return new ResourcesProvider(
-                ApkAssets.loadArscForLoader(fileDescriptor.getFileDescriptor()), assetsProvider);
-    }
-
-    /**
-     * Creates a ResourcesProvider from a resources table ({@code .arsc}) file representation in
-     * memory.
-     *
-     * @param sharedMemory the shared memory containing the data of the resources table to load
-     * @param assetsProvider the assets provider that overrides the loading of file-based resources
-     */
-    @NonNull
-    public static ResourcesProvider loadFromTable(@NonNull SharedMemory sharedMemory,
-            @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 @@
         }
 
         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 491d4a6..44f8a31 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -37,8 +37,21 @@
   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 @@
   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 @@
     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) {
-    return 0;
-  }
-
-  ATRACE_NAME(base::StringPrintf("LoadApkAssetsArsc(%s)", path.c_str()).c_str());
-
-  std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadArsc(path.c_str(), for_loader);
-
-  if (apk_assets == nullptr) {
-    std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
-    jniThrowException(env, "java/io/IOException", error_msg.c_str());
-    return 0;
-  }
-  return reinterpret_cast<jlong>(apk_assets.release());
-}
-
-static jlong NativeLoadArscFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
-                                  jstring friendly_name, jboolean for_loader) {
+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;
   }
 
+  if (offset < 0) {
+    jniThrowException(env, "java/lang/IllegalArgumentException",
+                     "offset cannot be negative");
+    return 0;
+  }
+
+  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 @@
     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 @@
 
 // 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 b2b0ec2..946fcc0 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -41,28 +41,85 @@
 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 @@
     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);
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
-                                                     bool for_loader) {
-  return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
-}
-
-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<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) {
-    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);
-}
-
-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);
-  }
-
+  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 APK '" << path << "' " << ::ErrorCodeString(result);
+    LOG(ERROR) << "Failed to open overlay APK '" << overlay_path << "' "
+               << ::ErrorCodeString(result);
     ::CloseArchive(unmanaged_handle);
     return {};
   }
 
-  time_t last_mod_time = getFileModDate(path.c_str());
+  return LoadImpl(unmanaged_handle, overlay_path, std::move(idmap_asset), std::move(loaded_idmap),
+                  flags | PROPERTY_OVERLAY);
+}
+
+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.ok()) {
+    LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+    return {};
+  }
+
+  return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
+                                                    const char* path,
+                                                    off64_t offset,
+                                                    off64_t length) {
+  CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+  CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+                                                 << kUnknownLength;
+  if (length == kUnknownLength) {
+    length = lseek64(fd, 0, SEEK_END);
+    if (length < 0) {
+      LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+                 << SystemErrorCodeToString(errno);
+      return {};
+    }
+  }
+
+  std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
+  if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+    LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
+               << SystemErrorCodeToString(errno);
+    return {};
+  }
+
+  // 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 @@
 
   // 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 @@
 
   // 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 @@
   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 @@
   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 @@
     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 @@
     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 c132f34..cd30c18 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -298,14 +298,13 @@
 /*
  * 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 @@
 }
 
 /*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 @@
  * 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 @@
 /*
  * 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 @@
     mMap = dataMap;
     mStart = -1;            // not used
     mLength = dataMap->getDataLength();
+    mFd = std::move(fd);
     assert(mOffset == 0);
 
     return NO_ERROR;
@@ -692,6 +692,17 @@
 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 e35c024..70bb441 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -749,7 +749,7 @@
 
 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 af802b0..643dc5c 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -37,49 +37,46 @@
 
 // Holds an APK.
 class ApkAssets {
+  // This means the data extends to the end of the file.
+  static constexpr off64_t kUnknownLength = -1;
+
  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);
-
-  // 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);
+  // 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 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 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 empty wrapper ApkAssets from the given file descriptor which points to an .arsc,
-  // Takes ownership of the file descriptor.
-  static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
-                                                   const std::string& friendly_name,
-                                                   bool for_loader = false);
+  // Creates an ApkAssets from the 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 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 @@
   }
 
   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 053dbb7..7576174 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 @@
      */
     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 @@
     /*
      * 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 @@
     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 b5d3a1f..89ff9f5 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -69,12 +69,24 @@
   }
 };
 
+// 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 0f2ee6f..26bf5ff 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -70,7 +70,7 @@
   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 35fea7a..ac32699 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -63,10 +63,12 @@
     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 c8dbe20..24361b5 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -67,7 +67,7 @@
 
 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 b679672..41ba637 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -221,8 +221,8 @@
 
 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(),