Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef40d2e8 authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

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
parent ad2d924b
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -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 {
  }
}
+2 −3
Original line number Diff line number Diff line
@@ -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) {
+1 −2
Original line number Diff line number Diff line
@@ -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,
+1 −1
Original line number Diff line number Diff line
@@ -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(
+147 −88
Original line number Diff line number Diff line
@@ -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);
Loading