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

Commit 714f94a7 authored by Ryan Mitchell's avatar Ryan Mitchell Committed by Automerger Merge Worker
Browse files

Refactor ApkAsset loading APIs am: ef40d2e8 am: a62b1d74 am: cca02f8b

Change-Id: Ia0ee58cbb63910ffc78add7c72cf0de6f0632d5e
parents d6847807 cca02f8b
Loading
Loading
Loading
Loading
+0 −3
Original line number Original line Diff line number Diff line
@@ -12897,11 +12897,8 @@ package android.content.res.loader {
    method @Nullable public android.content.res.loader.AssetsProvider getAssetsProvider();
    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) 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.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 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.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;
  }
  }
}
}
+2 −3
Original line number Original line Diff line number Diff line
@@ -346,10 +346,9 @@ public class ResourcesManager {


        // We must load this from disk.
        // We must load this from disk.
        if (overlay) {
        if (overlay) {
            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path),
            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/);
                    false /*system*/);
        } else {
        } else {
            apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
            apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
        }
        }


        if (mLoadedApkAssets != null) {
        if (mLoadedApkAssets != null) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -1443,7 +1443,7 @@ public class PackageParser {
        try {
        try {
            try {
            try {
                apkAssets = fd != null
                apkAssets = fd != null
                        ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
                        ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
                        : ApkAssets.loadFromPath(apkPath);
                        : ApkAssets.loadFromPath(apkPath);
            } catch (IOException e) {
            } catch (IOException e) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+1 −1
Original line number Original line Diff line number Diff line
@@ -220,7 +220,7 @@ public class ApkLiteParseUtils {
        try {
        try {
            try {
            try {
                apkAssets = fd != null
                apkAssets = fd != null
                        ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
                        ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
                        : ApkAssets.loadFromPath(apkPath);
                        : ApkAssets.loadFromPath(apkPath);
            } catch (IOException e) {
            } catch (IOException e) {
                throw new PackageParser.PackageParserException(
                throw new PackageParser.PackageParserException(
+147 −88
Original line number Original line Diff line number Diff line
@@ -15,17 +15,19 @@
 */
 */
package android.content.res;
package android.content.res;


import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.om.OverlayableInfo;
import android.content.om.OverlayableInfo;
import android.content.res.loader.ResourcesProvider;
import android.content.res.loader.ResourcesProvider;
import android.text.TextUtils;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;


import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.Objects;


/**
/**
@@ -39,14 +41,72 @@ import java.util.Objects;
 * @hide
 * @hide
 */
 */
public final class ApkAssets {
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
    @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.
     * 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.
     * @throws IOException if a disk I/O error or parsing error occurred.
     */
     */
    public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
    public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
        return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/,
        return loadFromPath(path, 0 /* flags */);
                false /*arscOnly*/, false /*forLoader*/);
    }
    }


    /**
    /**
     * Creates a new ApkAssets instance from the given path on disk.
     * Creates a new ApkAssets instance from the given path on disk.
     *
     *
     * @param path The path to an APK 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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @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 {
            throws IOException {
        return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
        return new ApkAssets(FORMAT_APK, path, flags);
                false /*arscOnly*/, false /*forLoader*/);
    }
    }


    /**
    /**
     * 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.
     * Performs a dup of the underlying fd, so you must take care of still closing
     * @param system When true, the APK is loaded as a system APK (framework).
     * the FileDescriptor yourself (and can do that whenever you want).
     * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
     *
     *                           loaded as a shared library.
     * @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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @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 loadFromFd(@NonNull FileDescriptor fd,
            boolean forceSharedLibrary) throws IOException {
            @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
        return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
        return new ApkAssets(FORMAT_APK, fd, friendlyName, flags);
                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
     * 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).
     * 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 APK.
     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
     * @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 offset The location within the file that the apk starts. This must be 0 if length is
     * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
     *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
     *                           loaded as a shared library.
     * @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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @throws IOException if a disk I/O error or parsing error occurred.
     */
     */
    public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
    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 {
            throws IOException {
        return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
        return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags);
                false /*forLoader*/);
    }
    }


    /**
    /**
@@ -116,99 +176,101 @@ public final class ApkAssets {
     * is encoded within the IDMAP.
     * is encoded within the IDMAP.
     *
     *
     * @param idmapPath Path to the IDMAP of an overlay APK.
     * @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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @throws IOException if a disk I/O error or parsing error occurred.
     */
     */
    public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
    public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
            throws IOException {
            @PropertyFlags int flags) throws IOException {
        return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
        return new ApkAssets(FORMAT_IDMAP, idmapPath, flags);
                false /*arscOnly*/, false /*forLoader*/);
    }
    }


    /**
    /**
     * Creates a new ApkAssets instance from the given path on disk for use with a
     * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
     * {@link ResourcesProvider}.
     * 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
     * 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).
     * 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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @throws IOException if a disk I/O error or parsing error occurred.
     */
     */
    @NonNull
    public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
    public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
            @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
        return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
        return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags);
                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}.
     * for use with a {@link ResourcesProvider}.
     *
     *
     * Performs a dup of the underlying fd, so you must take care of still closing
     * 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).
     * 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.
     * @return a new instance of ApkAssets.
     * @throws IOException if a disk I/O error or parsing error occurred.
     * @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 {
            throws IOException {
        return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
        return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags);
                false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
    }
    }


    /**
    /**
     * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
     * 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
     * is required for a lot of APIs, and it's easier to have a non-null reference rather than
     * tracking a separate identifier.
     * tracking a separate identifier.
     *
     * @param flags flags that change the behavior of loaded apk assets
     */
     */
    @NonNull
    @NonNull
    public static ApkAssets loadEmptyForLoader() {
    public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags) {
        return new ApkAssets(true);
        return new ApkAssets(flags);
    }
    }


    private ApkAssets(boolean forLoader) {
    private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags)
        mForLoader = forLoader;
            throws IOException {
        mNativePtr = nativeLoadEmpty(forLoader);
        Objects.requireNonNull(path, "path");
        mStringBlock = null;
        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,
    private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
            boolean arscOnly, boolean forLoader) throws IOException {
            @NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
        mForLoader = forLoader;
        Objects.requireNonNull(fd, "fd");
        Objects.requireNonNull(path, "path");
        Objects.requireNonNull(friendlyName, "friendlyName");
        mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
        mFlags = flags;
                : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
        mNativePtr = nativeLoadFd(format, fd, friendlyName, flags);
        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
    }
    }


    private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
    private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
            boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
            @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
        mForLoader = forLoader;
            throws IOException {
        Objects.requireNonNull(fd, "fd");
        Objects.requireNonNull(fd, "fd");
        Objects.requireNonNull(friendlyName, "friendlyName");
        Objects.requireNonNull(friendlyName, "friendlyName");
        mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
        mFlags = flags;
                : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
        mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags);
        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
    }
    }


    private ApkAssets(@PropertyFlags int flags) {
        mFlags = flags;
        mNativePtr = nativeLoadEmpty(flags);
        mStringBlock = null;
    }

    @UnsupportedAppUsage
    @UnsupportedAppUsage
    public @NonNull String getAssetPath() {
    public @NonNull String getAssetPath() {
        synchronized (this) {
        synchronized (this) {
@@ -226,8 +288,9 @@ public final class ApkAssets {
        }
        }
    }
    }


    /** Returns whether this apk assets was loaded using a {@link ResourcesProvider}. */
    public boolean isForLoader() {
    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,
    private static native long nativeLoad(@FormatType int format, @NonNull String path,
            boolean forceSharedLib, boolean overlay, boolean forLoader)
            @PropertyFlags int flags) throws IOException;
            throws IOException;
    private static native long nativeLoadEmpty(@PropertyFlags int flags);
    private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
    private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
            @NonNull String friendlyName, boolean system, boolean forceSharedLib,
            @NonNull String friendlyName, @PropertyFlags int flags) throws IOException;
            boolean forLoader)
    private static native long nativeLoadFdOffsets(@FormatType int format,
            throws IOException;
            @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
    private static native long nativeLoadArsc(@NonNull String path, boolean forLoader)
            @PropertyFlags int flags) throws IOException;
            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 void nativeDestroy(long ptr);
    private static native void nativeDestroy(long ptr);
    private static native @NonNull String nativeGetAssetPath(long ptr);
    private static native @NonNull String nativeGetAssetPath(long ptr);
    private static native long nativeGetStringBlock(long ptr);
    private static native long nativeGetStringBlock(long ptr);
Loading