Merge "[res] Correct, optimize adding shared lib assets" into main
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 273e40a..c0c1c31 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -32,6 +32,7 @@
import android.content.res.loader.ResourcesLoader;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -448,7 +449,7 @@
@Deprecated
@UnsupportedAppUsage
public int addAssetPath(String path) {
- return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(path), false, false, false);
}
/**
@@ -458,7 +459,7 @@
@Deprecated
@UnsupportedAppUsage
public int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+ return addAssetPathInternal(List.of(path), false, true, false);
}
/**
@@ -468,35 +469,103 @@
@Deprecated
@UnsupportedAppUsage
public int addOverlayPath(String path) {
- return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(path), true, false, false);
}
/**
* @hide
*/
- public void addSharedLibraryPaths(@NonNull String[] paths) {
- final int length = paths.length;
- for (int i = 0; i < length; i++) {
- addAssetPathInternal(paths[i], false, true);
+ public void addSharedLibraryPaths(@NonNull List<String> paths) {
+ addAssetPathInternal(paths, false, true, true);
+ }
+
+ private int addAssetPathInternal(List<String> paths, boolean overlay, boolean appAsLib,
+ boolean presetAssets) {
+ Objects.requireNonNull(paths, "paths");
+ if (paths.isEmpty()) {
+ return 0;
+ }
+
+ synchronized (this) {
+ ensureOpenLocked();
+
+ // See if we already have some of the paths loaded.
+ final int originalAssetsCount = mApkAssets.length;
+
+ // Getting an assets' path is a relatively expensive operation, cache them.
+ final ArrayMap<String, Integer> assetPaths = new ArrayMap<>(originalAssetsCount);
+ for (int i = 0; i < originalAssetsCount; i++) {
+ assetPaths.put(mApkAssets[i].getAssetPath(), i);
+ }
+
+ final ArrayList<String> newPaths = new ArrayList<>(paths.size());
+ int lastFoundIndex = -1;
+ for (int i = 0, pathsSize = paths.size(); i < pathsSize; i++) {
+ final var path = paths.get(i);
+ final int index = assetPaths.getOrDefault(path, -1);
+ if (index < 0) {
+ newPaths.add(path);
+ } else {
+ lastFoundIndex = index;
+ }
+ }
+ if (newPaths.isEmpty()) {
+ return lastFoundIndex + 1;
+ }
+
+ final var newAssets = loadAssets(newPaths, overlay, appAsLib);
+ if (newAssets.isEmpty()) {
+ return 0;
+ }
+ mApkAssets = makeNewAssetsArrayLocked(newAssets);
+ nativeSetApkAssets(mObject, mApkAssets, true, presetAssets);
+ invalidateCachesLocked(-1);
+ return originalAssetsCount + 1;
}
}
- private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
- Objects.requireNonNull(path, "path");
- synchronized (this) {
- ensureOpenLocked();
- final int count = mApkAssets.length;
-
- // See if we already have it loaded.
- for (int i = 0; i < count; i++) {
- if (mApkAssets[i].getAssetPath().equals(path)) {
- return i + 1;
- }
+ /**
+ * Insert the new assets preserving the correct order: all non-loader assets go before all
+ * of the loader assets.
+ */
+ @GuardedBy("this")
+ private @NonNull ApkAssets[] makeNewAssetsArrayLocked(
+ @NonNull ArrayList<ApkAssets> newNonLoaderAssets) {
+ final int originalAssetsCount = mApkAssets.length;
+ int firstLoaderIndex = originalAssetsCount;
+ for (int i = 0; i < originalAssetsCount; i++) {
+ if (mApkAssets[i].isForLoader()) {
+ firstLoaderIndex = i;
+ break;
}
+ }
+ final int newAssetsSize = newNonLoaderAssets.size();
+ final var newAssetsArray = new ApkAssets[originalAssetsCount + newAssetsSize];
+ if (firstLoaderIndex > 0) {
+ // This should always be true, but who knows...
+ System.arraycopy(mApkAssets, 0, newAssetsArray, 0, firstLoaderIndex);
+ }
+ for (int i = 0; i < newAssetsSize; i++) {
+ newAssetsArray[firstLoaderIndex + i] = newNonLoaderAssets.get(i);
+ }
+ if (originalAssetsCount > firstLoaderIndex) {
+ System.arraycopy(
+ mApkAssets, firstLoaderIndex,
+ newAssetsArray, firstLoaderIndex + newAssetsSize,
+ originalAssetsCount - firstLoaderIndex);
+ }
+ return newAssetsArray;
+ }
- final ApkAssets assets;
+ private static @NonNull ArrayList<ApkAssets> loadAssets(@NonNull ArrayList<String> paths,
+ boolean overlay, boolean appAsLib) {
+ final int pathsSize = paths.size();
+ final var loadedAssets = new ArrayList<ApkAssets>(pathsSize);
+ for (int i = 0; i < pathsSize; i++) {
+ final var path = paths.get(i);
try {
- if (overlay) {
+ final ApkAssets assets;
+ if (overlay || path.endsWith(".frro")) {
// TODO(b/70343104): This hardcoded path will be removed once
// addAssetPathInternal is deleted.
final String idmapPath = "/data/resource-cache/"
@@ -507,16 +576,12 @@
assets = ApkAssets.loadFromPath(path,
appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
+ loadedAssets.add(assets);
} catch (IOException e) {
- return 0;
+ Log.w(TAG, "Failed to load asset, path = " + path, e);
}
-
- mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
- mApkAssets[count] = assets;
- nativeSetApkAssets(mObject, mApkAssets, true, false);
- invalidateCachesLocked(-1);
- return count + 1;
}
+ return loadedAssets;
}
/** @hide */
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 31cacb7..9f8b974 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -49,6 +49,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -71,6 +72,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@@ -205,13 +207,21 @@
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
if (Flags.registerResourcePaths()) {
- ArrayMap<String, SharedLibraryAssets> sharedLibMap =
+ final ArraySet<String> uniquePaths = new ArraySet<>();
+ final ArrayList<String> orderedPaths = new ArrayList<>();
+ final ArrayMap<String, SharedLibraryAssets> sharedLibMap =
ResourcesManager.getInstance().getSharedLibAssetsMap();
final int size = sharedLibMap.size();
for (int i = 0; i < size; i++) {
- assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths());
+ final var paths = sharedLibMap.valueAt(i).getAllAssetPaths();
+ for (int j = 0; j < paths.length; j++) {
+ if (uniquePaths.add(paths[j])) {
+ orderedPaths.add(paths[j]);
+ }
+ }
}
- mSharedLibCount = sharedLibMap.size();
+ assets.addSharedLibraryPaths(orderedPaths);
+ mSharedLibCount = size;
}
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;