diff --git a/api/current.txt b/api/current.txt index 5ae65901fec8b..8a35cc779ceb1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { @@ -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; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5ade261532962..24589cfec27a9 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1441,7 +1441,7 @@ public class PackageParser { 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, diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 88b4c290c52ae..27399e4b39bc6 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -220,7 +220,7 @@ public class ApkLiteParseUtils { try { try { apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */) + ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */) : 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 078d175b9a80f..bc418061e1d11 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; 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; @@ -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. * @@ -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); } /** @@ -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); } /** @@ -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); } /** @@ -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 */); } /** @@ -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); } /** @@ -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); } /** @@ -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); } /** @@ -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 { - 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(@FormatType int format, @NonNull FileDescriptor fd, - @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags) + @NonNull String friendlyName, @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 = nativeLoadFd(format, fd, friendlyName, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + mAssets = assets; } - private ApkAssets(@PropertyFlags int flags) { + private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, + @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 = nativeLoadEmpty(flags); + mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + mAssets = assets; + } + + private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) { + mFlags = flags; + mNativePtr = nativeLoadEmpty(flags, assets); mStringBlock = null; + mAssets = assets; } @UnsupportedAppUsage @@ -311,6 +344,14 @@ public final class ApkAssets { 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 * NOT a full AssetManager. This means that shared-library references will not be @@ -382,13 +423,15 @@ public final class ApkAssets { } 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); diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 6b9613d6e3be4..7b2b93949a9fb 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -27,9 +27,7 @@ import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; -import android.content.res.loader.AssetsProvider; import android.content.res.loader.ResourcesLoader; -import android.content.res.loader.ResourcesProvider; import android.os.ParcelFileDescriptor; import android.util.ArraySet; import android.util.Log; @@ -44,7 +42,6 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -828,13 +825,6 @@ public final class AssetManager implements AutoCloseable { Objects.requireNonNull(fileName, "fileName"); synchronized (this) { ensureOpenLocked(); - - String path = Paths.get("assets", fileName).toString(); - InputStream inputStream = searchLoaders(0, path, accessMode); - if (inputStream != null) { - return inputStream; - } - final long asset = nativeOpenAsset(mObject, fileName, accessMode); if (asset == 0) { throw new FileNotFoundException("Asset file: " + fileName); @@ -859,13 +849,6 @@ public final class AssetManager implements AutoCloseable { Objects.requireNonNull(fileName, "fileName"); synchronized (this) { ensureOpenLocked(); - - String path = Paths.get("assets", fileName).toString(); - AssetFileDescriptor fileDescriptor = searchLoadersFd(0, path); - if (fileDescriptor != null) { - return fileDescriptor; - } - final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); if (pfd == null) { throw new FileNotFoundException("Asset file: " + fileName); @@ -959,12 +942,6 @@ public final class AssetManager implements AutoCloseable { Objects.requireNonNull(fileName, "fileName"); synchronized (this) { ensureOpenLocked(); - - InputStream inputStream = searchLoaders(cookie, fileName, accessMode); - if (inputStream != null) { - return inputStream; - } - final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); if (asset == 0) { throw new FileNotFoundException("Asset absolute file: " + fileName); @@ -1004,12 +981,6 @@ public final class AssetManager implements AutoCloseable { Objects.requireNonNull(fileName, "fileName"); synchronized (this) { ensureOpenLocked(); - - AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName); - if (fileDescriptor != null) { - return fileDescriptor; - } - final ParcelFileDescriptor pfd = nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); if (pfd == null) { @@ -1072,15 +1043,7 @@ public final class AssetManager implements AutoCloseable { synchronized (this) { ensureOpenLocked(); - final long xmlBlock; - AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName); - if (fileDescriptor != null) { - xmlBlock = nativeOpenXmlAssetFd(mObject, cookie, - fileDescriptor.getFileDescriptor()); - } else { - xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); - } - + final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); if (xmlBlock == 0) { throw new FileNotFoundException("Asset XML file: " + fileName); } @@ -1090,122 +1053,6 @@ public final class AssetManager implements AutoCloseable { } } - private ResourcesProvider findResourcesProvider(int assetCookie) { - if (mLoaders == null) { - return null; - } - - int apkAssetsIndex = assetCookie - 1; - if (apkAssetsIndex >= mApkAssets.length || apkAssetsIndex < 0) { - return null; - } - - final ApkAssets apkAssets = mApkAssets[apkAssetsIndex]; - if (!apkAssets.isForLoader()) { - return null; - } - - for (int i = mLoaders.length - 1; i >= 0; i--) { - final ResourcesLoader loader = mLoaders[i]; - for (int j = 0, n = loader.getProviders().size(); j < n; j++) { - final ResourcesProvider provider = loader.getProviders().get(j); - if (apkAssets == provider.getApkAssets()) { - return provider; - } - } - } - - return null; - } - - private InputStream searchLoaders(int cookie, @NonNull String fileName, int accessMode) - throws IOException { - if (mLoaders == null) { - return null; - } - - if (cookie == 0) { - // A cookie of 0 means no specific ApkAssets, so search everything - for (int i = mLoaders.length - 1; i >= 0; i--) { - final ResourcesLoader loader = mLoaders[i]; - final List providers = loader.getProviders(); - for (int j = providers.size() - 1; j >= 0; j--) { - final AssetsProvider assetsProvider = providers.get(j).getAssetsProvider(); - if (assetsProvider == null) { - continue; - } - - try { - final InputStream inputStream = assetsProvider.loadAsset( - fileName, accessMode); - if (inputStream != null) { - return inputStream; - } - } catch (IOException ignored) { - // When searching, ignore read failures - } - } - } - - return null; - } - - final ResourcesProvider provider = findResourcesProvider(cookie); - if (provider != null && provider.getAssetsProvider() != null) { - return provider.getAssetsProvider().loadAsset( - fileName, accessMode); - } - - return null; - } - - private AssetFileDescriptor searchLoadersFd(int cookie, @NonNull String fileName) - throws IOException { - if (mLoaders == null) { - return null; - } - - if (cookie == 0) { - // A cookie of 0 means no specific ApkAssets, so search everything - for (int i = mLoaders.length - 1; i >= 0; i--) { - final ResourcesLoader loader = mLoaders[i]; - final List providers = loader.getProviders(); - for (int j = providers.size() - 1; j >= 0; j--) { - final AssetsProvider assetsProvider = providers.get(j).getAssetsProvider(); - if (assetsProvider == null) { - continue; - } - - try { - final ParcelFileDescriptor fileDescriptor = assetsProvider - .loadAssetParcelFd(fileName); - if (fileDescriptor != null) { - return new AssetFileDescriptor(fileDescriptor, 0, - AssetFileDescriptor.UNKNOWN_LENGTH); - } - } catch (IOException ignored) { - // When searching, ignore read failures - } - } - } - - return null; - } - - final ResourcesProvider provider = findResourcesProvider(cookie); - if (provider != null && provider.getAssetsProvider() != null) { - final ParcelFileDescriptor fileDescriptor = provider.getAssetsProvider() - .loadAssetParcelFd(fileName); - if (fileDescriptor != null) { - return new AssetFileDescriptor(fileDescriptor, 0, - AssetFileDescriptor.UNKNOWN_LENGTH); - } - return null; - } - - return null; - } - void xmlBlockGone(int id) { synchronized (this) { decRefsLocked(id); diff --git a/core/java/android/content/res/loader/AssetsProvider.java b/core/java/android/content/res/loader/AssetsProvider.java index c315494cf7281..0f8f1d1da8d24 100644 --- a/core/java/android/content/res/loader/AssetsProvider.java +++ b/core/java/android/content/res/loader/AssetsProvider.java @@ -18,12 +18,10 @@ package android.content.res.loader; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.os.ParcelFileDescriptor; -import java.io.IOException; -import java.io.InputStream; - /** * Provides callbacks that allow for the value of a file-based resources or assets of a * {@link ResourcesProvider} to be specified or overridden. @@ -34,6 +32,10 @@ public interface AssetsProvider { * Callback that allows the value of a file-based resources or asset to be specified or * overridden. * + *

The system will take ownership of the file descriptor returned from this method, so + * {@link ParcelFileDescriptor#dup() dup} the file descriptor before returning if the system + * should not own it. + * *

There are two situations in which this method will be called: *