Skip to content

Commit

Permalink
Move AssetsProvider to native layer
Browse files Browse the repository at this point in the history
Querying in the native layer for assets provided through
AssetsProviders does not currently work. This change refactors the
AssetProvider API to return a file descriptor that is read in the
native layer and can bubble up to the java layer.

This change also removes the InputStream API to favor of developers
using memfd_create.

Bug: 142716192
Test: atest ResourceLoaderValuesTest
Change-Id: I1a7eca0994c3b7cc32008d9a72bf91086ff0e816
  • Loading branch information
RyanMitch16 committed Mar 20, 2020
1 parent c07aa70 commit 4ea1e42
Show file tree
Hide file tree
Showing 21 changed files with 672 additions and 762 deletions.
10 changes: 1 addition & 9 deletions api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12846,14 +12846,7 @@ package android.content.res {
package android.content.res.loader {

public interface AssetsProvider {
method @Nullable public default java.io.InputStream loadAsset(@NonNull String, int) throws java.io.IOException;
method @Nullable public default android.os.ParcelFileDescriptor loadAssetParcelFd(@NonNull String) throws java.io.IOException;
}

public class DirectoryAssetsProvider implements android.content.res.loader.AssetsProvider {
ctor public DirectoryAssetsProvider(@NonNull java.io.File);
method @Nullable public java.io.File findFile(@NonNull String);
method @NonNull public java.io.File getDirectory();
method @Nullable public default android.content.res.AssetFileDescriptor loadAssetFd(@NonNull String, int);
}

public class ResourcesLoader {
Expand All @@ -12868,7 +12861,6 @@ package android.content.res.loader {
public class ResourcesProvider implements java.lang.AutoCloseable java.io.Closeable {
method public void close();
method @NonNull public static android.content.res.loader.ResourcesProvider empty(@NonNull android.content.res.loader.AssetsProvider);
method @Nullable public android.content.res.loader.AssetsProvider getAssetsProvider();
method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.ParcelFileDescriptor, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
method @NonNull public static android.content.res.loader.ResourcesProvider loadFromDirectory(@NonNull String, @Nullable android.content.res.loader.AssetsProvider) throws java.io.IOException;
Expand Down
2 changes: 1 addition & 1 deletion core/java/android/content/pm/PackageParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String
try {
try {
apkAssets = fd != null
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private static PackageParser.ApkLite parseApkLiteInner(File apkFile, FileDescrip
try {
try {
apkAssets = fd != null
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */)
? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
throw new PackageParser.PackageParserException(
Expand Down
101 changes: 72 additions & 29 deletions core/java/android/content/res/ApkAssets.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.om.OverlayableInfo;
import android.content.res.loader.AssetsProvider;
import android.content.res.loader.ResourcesProvider;

import com.android.internal.annotations.GuardedBy;
Expand Down Expand Up @@ -112,6 +113,9 @@ public final class ApkAssets {
@PropertyFlags
private final int mFlags;

@Nullable
private final AssetsProvider mAssets;

/**
* Creates a new ApkAssets instance from the given path on disk.
*
Expand All @@ -133,7 +137,21 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
throws IOException {
return new ApkAssets(FORMAT_APK, path, flags);
return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
}

/**
* Creates a new ApkAssets instance from the given path on disk.
*
* @param path The path to an APK on disk.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
return new ApkAssets(FORMAT_APK, path, flags, assets);
}

/**
Expand All @@ -145,12 +163,14 @@ public final class ApkAssets {
* @param fd The FileDescriptor of an open, readable APK.
* @param friendlyName The friendly name used to identify this ApkAssets when logging.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
return new ApkAssets(FORMAT_APK, fd, friendlyName, flags);
@NonNull String friendlyName, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
}

/**
Expand All @@ -166,13 +186,15 @@ public final class ApkAssets {
* @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
* if it extends to the end of the file.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets)
throws IOException {
return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags);
return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
}

/**
Expand All @@ -186,7 +208,7 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
@PropertyFlags int flags) throws IOException {
return new ApkAssets(FORMAT_IDMAP, idmapPath, flags);
return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
}

/**
Expand All @@ -199,12 +221,14 @@ public final class ApkAssets {
* @param fd The FileDescriptor of an open, readable resources.arsc.
* @param friendlyName The friendly name used to identify this ApkAssets when logging.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags);
@NonNull String friendlyName, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
}

/**
Expand All @@ -221,13 +245,14 @@ public final class ApkAssets {
* @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
* if it extends to the end of the file.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
throws IOException {
return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags);
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags, assets);
}

/**
Expand All @@ -236,12 +261,13 @@ public final class ApkAssets {
*
* @param path The path to a directory on disk.
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
* @return a new instance of ApkAssets.
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromDir(@NonNull String path,
@PropertyFlags int flags) throws IOException {
return new ApkAssets(FORMAT_DIR, path, flags);
@PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
return new ApkAssets(FORMAT_DIR, path, flags, assets);
}

/**
Expand All @@ -250,43 +276,50 @@ public final class ApkAssets {
* tracking a separate identifier.
*
* @param flags flags that change the behavior of loaded apk assets
* @param assets The assets provider that overrides the loading of file-based resources
*/
@NonNull
public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags) {
return new ApkAssets(flags);
public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
@Nullable AssetsProvider assets) {
return new ApkAssets(flags, assets);
}

private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags)
throws IOException {
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
Objects.requireNonNull(path, "path");
mFlags = flags;
mNativePtr = nativeLoad(format, path, flags);
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
mAssets = assets;
}

private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags) throws IOException {
@NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
throws IOException {
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
mFlags = flags;
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags);
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
mAssets = assets;
}

private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags)
throws IOException {
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
mFlags = flags;
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags);
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
mAssets = assets;
}

private ApkAssets(@PropertyFlags int flags) {
private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
mFlags = flags;
mNativePtr = nativeLoadEmpty(flags);
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
mAssets = assets;
}

@UnsupportedAppUsage
Expand All @@ -311,6 +344,14 @@ public boolean isForLoader() {
return (mFlags & PROPERTY_LOADER) != 0;
}

/**
* Returns the assets provider that overrides the loading of assets present in this apk assets.
*/
@Nullable
public AssetsProvider getAssetsProvider() {
return mAssets;
}

/**
* Retrieve a parser for a compiled XML file. This is associated with a single APK and
* <em>NOT</em> a full AssetManager. This means that shared-library references will not be
Expand Down Expand Up @@ -382,13 +423,15 @@ public void close() {
}

private static native long nativeLoad(@FormatType int format, @NonNull String path,
@PropertyFlags int flags) throws IOException;
private static native long nativeLoadEmpty(@PropertyFlags int flags);
@PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
private static native long nativeLoadEmpty(@PropertyFlags int flags,
@Nullable AssetsProvider asset);
private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags) throws IOException;
@NonNull String friendlyName, @PropertyFlags int flags,
@Nullable AssetsProvider asset) throws IOException;
private static native long nativeLoadFdOffsets(@FormatType int format,
@NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
@PropertyFlags int flags) throws IOException;
@PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
private static native void nativeDestroy(long ptr);
private static native @NonNull String nativeGetAssetPath(long ptr);
private static native long nativeGetStringBlock(long ptr);
Expand Down
Loading

0 comments on commit 4ea1e42

Please sign in to comment.