summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yurii Zubrytskyi <zyy@google.com> 2024-11-19 02:45:41 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-19 02:45:41 +0000
commit5c29154bafbaaa4bbe9598d4de27b7c99d1b6b0f (patch)
tree072d8ff3c85c72e0d13534054aad8de7b761ce5c
parentc2701705b16af19e0e528c0f3fed38432128d002 (diff)
parentbf6b187a3ddc1a24098b099ba81659a7796c7a4d (diff)
Merge "Reapply "[res] Duplicate AssetManager when changes are needed"" into main
-rw-r--r--core/java/android/app/ResourcesManager.java124
-rw-r--r--core/java/android/content/res/ApkAssets.java33
-rw-r--r--core/java/android/content/res/ResourcesImpl.java22
3 files changed, 133 insertions, 46 deletions
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 599a46b131d5..3cffca796680 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -174,22 +174,58 @@ public class ResourcesManager {
}
/**
- * Apply the registered library paths to the passed impl object
- * @return the hash code for the current version of the registered paths
+ * Apply the registered library paths to the passed AssetManager. If may create a new
+ * AssetManager if any changes are needed and it isn't allowed to reuse the old one.
+ *
+ * @return new AssetManager and the hash code for the current version of the registered paths
*/
- public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+ public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs(
+ @NonNull AssetManager assets, boolean reuseAssets) {
if (!Flags.registerResourcePaths()) {
- return 0;
+ return new Pair<>(assets, 0);
}
- final var collector = new PathCollector(null);
- final int size = mSharedLibAssetsMap.size();
- for (int i = 0; i < size; i++) {
- final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
- collector.appendKey(libraryKey);
+ final int size;
+ final PathCollector collector;
+
+ synchronized (mLock) {
+ size = mSharedLibAssetsMap.size();
+ if (assets == AssetManager.getSystem()) {
+ return new Pair<>(assets, size);
+ }
+ collector = new PathCollector(resourcesKeyFromAssets(assets));
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
+ }
}
- impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
- return size;
+ if (collector.isSameAsOriginal()) {
+ return new Pair<>(assets, size);
+ }
+ if (reuseAssets) {
+ assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return new Pair<>(assets, size);
+ }
+ final var newAssetsBuilder = new AssetManager.Builder().setNoInit();
+ for (final var asset : assets.getApkAssets()) {
+ // Skip everything that's either default, or will get added by the collector (builder
+ // doesn't check for duplicates at all).
+ if (asset.isSystem() || asset.isForLoader() || asset.isOverlay()
+ || asset.isSharedLib()) {
+ continue;
+ }
+ newAssetsBuilder.addApkAssets(asset);
+ }
+ for (final var key : extractApkKeys(collector.collectedKey())) {
+ try {
+ final var asset = loadApkAssets(key);
+ newAssetsBuilder.addApkAssets(asset);
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't load assets for key " + key, e);
+ }
+ }
+ assets.getLoaders().forEach(newAssetsBuilder::addLoader);
+ return new Pair<>(newAssetsBuilder.build(), size);
}
public static class ApkKey {
@@ -624,6 +660,23 @@ public class ResourcesManager {
return apkKeys;
}
+ private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) {
+ final var libs = new ArrayList<String>();
+ final var overlays = new ArrayList<String>();
+ for (final ApkAssets asset : assets.getApkAssets()) {
+ if (asset.isSystem() || asset.isForLoader()) {
+ continue;
+ }
+ if (asset.isOverlay()) {
+ overlays.add(asset.getAssetPath());
+ } else if (asset.isSharedLib()) {
+ libs.add(asset.getAssetPath());
+ }
+ }
+ return new ResourcesKey(null, null, overlays.toArray(new String[0]),
+ libs.toArray(new String[0]), 0, null, null);
+ }
+
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -752,7 +805,7 @@ public class ResourcesManager {
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
- final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
+ final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -1832,31 +1885,32 @@ public class ResourcesManager {
for (int i = 0; i < resourcesCount; i++) {
final WeakReference<Resources> ref = mAllResourceReferences.get(i);
final Resources r = ref != null ? ref.get() : null;
- if (r != null) {
- final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
- if (key != null) {
- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
- if (impl == null) {
- throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
- }
- r.setImpl(impl);
- } else {
- // ResourcesKey is null which means the ResourcesImpl could belong to a
- // Resources created by application through Resources constructor and was not
- // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
- // have shared library asset paths appended if there are any.
- if (r.getImpl() != null) {
- final ResourcesImpl oldImpl = r.getImpl();
- final AssetManager oldAssets = oldImpl.getAssets();
- // ResourcesImpl constructor will help to append shared library asset paths.
- if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
- oldImpl.getMetrics(), oldImpl.getConfiguration(),
- oldImpl.getDisplayAdjustments());
+ if (r == null) {
+ continue;
+ }
+ final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+ if (key != null) {
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+ }
+ r.setImpl(impl);
+ } else {
+ // ResourcesKey is null which means the ResourcesImpl could belong to a
+ // Resources created by application through Resources constructor and was not
+ // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
+ // have shared library asset paths appended if there are any.
+ final ResourcesImpl oldImpl = r.getImpl();
+ if (oldImpl != null) {
+ final AssetManager oldAssets = oldImpl.getAssets();
+ // ResourcesImpl constructor will help to append shared library asset paths.
+ if (oldAssets != AssetManager.getSystem()) {
+ if (oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldImpl);
r.setImpl(newImpl);
} else {
- Slog.w(TAG, "Skip appending shared library asset paths for the "
- + "Resource as its assets are not up to date.");
+ Slog.w(TAG, "Skip appending shared library asset paths for "
+ + "the Resources as its assets are not up to date.");
}
}
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 68b5d782bfbf..908999b64961 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -124,11 +124,13 @@ public final class ApkAssets {
@Nullable
@GuardedBy("this")
- private final StringBlock mStringBlock; // null or closed if mNativePtr = 0.
+ private StringBlock mStringBlock; // null or closed if mNativePtr = 0.
@PropertyFlags
private final int mFlags;
+ private final boolean mIsOverlay;
+
@Nullable
private final AssetsProvider mAssets;
@@ -302,40 +304,43 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(path, "path");
- mFlags = 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, @Nullable AssetsProvider assets)
throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = 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,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = 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;
+ this(FORMAT_APK, flags, assets);
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
+ }
+
+ private ApkAssets(@FormatType int format, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) {
+ mFlags = flags;
mAssets = assets;
+ mIsOverlay = format == FORMAT_IDMAP;
}
@UnsupportedAppUsage
@@ -425,6 +430,18 @@ public final class ApkAssets {
}
}
+ public boolean isSystem() {
+ return (mFlags & PROPERTY_SYSTEM) != 0;
+ }
+
+ public boolean isSharedLib() {
+ return (mFlags & PROPERTY_DYNAMIC) != 0;
+ }
+
+ public boolean isOverlay() {
+ return mIsOverlay;
+ }
+
@Override
public String toString() {
return "ApkAssets{path=" + getDebugName() + "}";
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index e6b93427f413..bcaceb24d767 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,9 +203,25 @@ public class ResourcesImpl {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
- mAssets = assets;
- mAppliedSharedLibsHash =
- ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
+ // Don't reuse assets by default as we have no control over whether they're already
+ // inside some other ResourcesImpl.
+ this(assets, metrics, config, displayAdjustments, false);
+ }
+
+ public ResourcesImpl(@NonNull ResourcesImpl orig) {
+ // We know for sure that the other assets are in use, so can't reuse the object here.
+ this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(),
+ orig.getDisplayAdjustments(), false);
+ }
+
+ public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
+ @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments,
+ boolean reuseAssets) {
+ final var assetsAndHash =
+ ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets,
+ reuseAssets);
+ mAssets = assetsAndHash.first;
+ mAppliedSharedLibsHash = assetsAndHash.second;
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();