Refactor AssetManager
Bug: 64071469
Test: atest CtsContentTestCases
Change-Id: Ia6856157e8813856268fba003e1e591d690cb26e
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8d70a55..0521155 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -526,41 +526,22 @@
Landroid/content/res/AssetFileDescriptor;->mStartOffset:J
Landroid/content/res/AssetManager;->addAssetPathAsSharedLibrary(Ljava/lang/String;)I
Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
-Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I
-Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
-Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V
-Landroid/content/res/AssetManager;->ensureStringBlocks()[Landroid/content/res/StringBlock;
-Landroid/content/res/AssetManager;->getArraySize(I)I
Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
-Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String;
-Landroid/content/res/AssetManager;->getNativeStringBlock(I)J
Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
Landroid/content/res/AssetManager;->getResourceEntryName(I)Ljava/lang/String;
Landroid/content/res/AssetManager;->getResourceIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
Landroid/content/res/AssetManager;->getResourceName(I)Ljava/lang/String;
Landroid/content/res/AssetManager;->getResourcePackageName(I)Ljava/lang/String;
Landroid/content/res/AssetManager;->getResourceTypeName(I)Ljava/lang/String;
-Landroid/content/res/AssetManager;->getStringBlockCount()I
Landroid/content/res/AssetManager;-><init>()V
Landroid/content/res/AssetManager;->isUpToDate()Z
-Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I
-Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I
-Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I
Landroid/content/res/AssetManager;->mObject:J
-Landroid/content/res/AssetManager;->mStringBlocks:[Landroid/content/res/StringBlock;
-Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream;
-Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J
-Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J
Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
-Landroid/content/res/AssetManager;->retrieveArray(I[I)I
-Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z
Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
-Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I
-Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I
Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
Landroid/content/res/ColorStateList;->mColors:[I
Landroid/content/res/ColorStateList;->mDefaultColor:I
@@ -602,7 +583,6 @@
Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme;
Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue;
Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser;
-Landroid/content/res/XmlBlock;->close()V
Landroid/content/res/XmlBlock;-><init>([B)V
Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser;
Landroid/content/res/XmlBlock$Parser;->mParseState:J
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3d04e2c..99c5d2b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6366,6 +6366,8 @@
} else {
writer.print(prefix); writer.println("No AutofillManager");
}
+
+ ResourcesManager.getInstance().dump(prefix, writer);
}
/**
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fb11272..fc5ea66 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.CompatResources;
import android.content.res.CompatibilityInfo;
@@ -34,6 +35,7 @@
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.LruCache;
import android.util.Pair;
import android.util.Slog;
import android.view.Display;
@@ -41,9 +43,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Predicate;
@@ -59,12 +65,7 @@
* Predicate that returns true if a WeakReference is gc'ed.
*/
private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
- new Predicate<WeakReference<Resources>>() {
- @Override
- public boolean test(WeakReference<Resources> weakRef) {
- return weakRef == null || weakRef.get() == null;
- }
- };
+ weakRef -> weakRef == null || weakRef.get() == null;
/**
* The global compatibility settings.
@@ -89,6 +90,48 @@
*/
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
+ private static class ApkKey {
+ public final String path;
+ public final boolean sharedLib;
+ public final boolean overlay;
+
+ ApkKey(String path, boolean sharedLib, boolean overlay) {
+ this.path = path;
+ this.sharedLib = sharedLib;
+ this.overlay = overlay;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + this.path.hashCode();
+ result = 31 * result + Boolean.hashCode(this.sharedLib);
+ result = 31 * result + Boolean.hashCode(this.overlay);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ApkKey)) {
+ return false;
+ }
+ ApkKey other = (ApkKey) obj;
+ return this.path.equals(other.path) && this.sharedLib == other.sharedLib
+ && this.overlay == other.overlay;
+ }
+ }
+
+ /**
+ * The ApkAssets we are caching and intend to hold strong references to.
+ */
+ private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(15);
+
+ /**
+ * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
+ * in our LRU cache. Bonus resources :)
+ */
+ private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
+
/**
* Resources and base configuration override associated with an Activity.
*/
@@ -260,6 +303,43 @@
}
}
+ private static String overlayPathToIdmapPath(String path) {
+ return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
+ }
+
+ private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
+ throws IOException {
+ final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
+ ApkAssets apkAssets = mLoadedApkAssets.get(newKey);
+ if (apkAssets != null) {
+ return apkAssets;
+ }
+
+ // Optimistically check if this ApkAssets exists somewhere else.
+ final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
+ if (apkAssetsRef != null) {
+ apkAssets = apkAssetsRef.get();
+ if (apkAssets != null) {
+ mLoadedApkAssets.put(newKey, apkAssets);
+ return apkAssets;
+ } else {
+ // Clean up the reference.
+ mCachedApkAssets.remove(newKey);
+ }
+ }
+
+ // We must load this from disk.
+ if (overlay) {
+ apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path),
+ false /*system*/);
+ } else {
+ apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
+ }
+ mLoadedApkAssets.put(newKey, apkAssets);
+ mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
+ return apkAssets;
+ }
+
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -270,13 +350,16 @@
*/
@VisibleForTesting
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
- AssetManager assets = new AssetManager();
+ final AssetManager.Builder builder = new AssetManager.Builder();
// resDir can be null if the 'android' package is creating a new Resources object.
// This is fine, since each AssetManager automatically loads the 'android' package
// already.
if (key.mResDir != null) {
- if (assets.addAssetPath(key.mResDir) == 0) {
+ try {
+ builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
+ false /*overlay*/));
+ } catch (IOException e) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
@@ -284,7 +367,10 @@
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
- if (assets.addAssetPath(splitResDir) == 0) {
+ try {
+ builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
+ false /*overlay*/));
+ } catch (IOException e) {
Log.e(TAG, "failed to add split asset path " + splitResDir);
return null;
}
@@ -293,7 +379,14 @@
if (key.mOverlayDirs != null) {
for (final String idmapPath : key.mOverlayDirs) {
- assets.addOverlayPath(idmapPath);
+ try {
+ builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
+ true /*overlay*/));
+ } catch (IOException e) {
+ Log.w(TAG, "failed to add overlay path " + idmapPath);
+
+ // continue.
+ }
}
}
@@ -302,14 +395,73 @@
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
- if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
+ try {
+ builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
+ false /*overlay*/));
+ } catch (IOException e) {
Log.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
+
+ // continue.
}
}
}
}
- return assets;
+
+ return builder.build();
+ }
+
+ private static <T> int countLiveReferences(Collection<WeakReference<T>> collection) {
+ int count = 0;
+ for (WeakReference<T> ref : collection) {
+ final T value = ref != null ? ref.get() : null;
+ if (value != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * @hide
+ */
+ public void dump(String prefix, PrintWriter printWriter) {
+ synchronized (this) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ for (int i = 0; i < prefix.length() / 2; i++) {
+ pw.increaseIndent();
+ }
+
+ pw.println("ResourcesManager:");
+ pw.increaseIndent();
+ pw.print("cached apks: total=");
+ pw.print(mLoadedApkAssets.size());
+ pw.print(" created=");
+ pw.print(mLoadedApkAssets.createCount());
+ pw.print(" evicted=");
+ pw.print(mLoadedApkAssets.evictionCount());
+ pw.print(" hit=");
+ pw.print(mLoadedApkAssets.hitCount());
+ pw.print(" miss=");
+ pw.print(mLoadedApkAssets.missCount());
+ pw.print(" max=");
+ pw.print(mLoadedApkAssets.maxSize());
+ pw.println();
+
+ pw.print("total apks: ");
+ pw.println(countLiveReferences(mCachedApkAssets.values()));
+
+ pw.print("resources: ");
+
+ int references = countLiveReferences(mResourceReferences);
+ for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+ references += countLiveReferences(activityResources.activityResources);
+ }
+ pw.println(references);
+
+ pw.print("resource impls: ");
+ pw.println(countLiveReferences(mResourceImpls.values()));
+ }
}
private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
@@ -630,28 +782,16 @@
// We will create the ResourcesImpl object outside of holding this lock.
}
- }
- // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
- if (resourcesImpl == null) {
- return null;
- }
-
- synchronized (this) {
- ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
- if (existingResourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
- + " new impl=" + resourcesImpl);
- }
- resourcesImpl.getAssets().close();
- resourcesImpl = existingResourcesImpl;
- } else {
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+ // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
+ ResourcesImpl resourcesImpl = createResourcesImpl(key);
+ if (resourcesImpl == null) {
+ return null;
}
+ // Add this ResourcesImpl to the cache.
+ mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+
final Resources resources;
if (activityToken != null) {
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2420b63..6373a11 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -1287,7 +1288,6 @@
*/
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
- final AssetManager assets = newConfiguredAssetManager();
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
@@ -1296,8 +1296,9 @@
}
}
+ final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
- final Package pkg = parseBaseApk(apkFile, assets, flags);
+ final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
@@ -1305,28 +1306,10 @@
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
- IoUtils.closeQuietly(assets);
+ IoUtils.closeQuietly(assetLoader);
}
}
- private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
- throws PackageParserException {
- if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + apkPath);
- }
-
- // The AssetManager guarantees uniqueness for asset paths, so if this asset path
- // already exists in the AssetManager, addAssetPath will only return the cookie
- // assigned to it.
- int cookie = assets.addAssetPath(apkPath);
- if (cookie == 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
- }
- return cookie;
- }
-
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
@@ -1342,13 +1325,15 @@
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
- final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
- Resources res = null;
XmlResourceParser parser = null;
try {
- res = new Resources(assets, mMetrics, null);
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
@@ -1383,15 +1368,18 @@
if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
- final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
final Resources res;
XmlResourceParser parser = null;
try {
- res = new Resources(assets, mMetrics, null);
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ // This must always succeed, as the path has been added to the AssetManager before.
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
@@ -1593,21 +1581,19 @@
int flags) throws PackageParserException {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
- AssetManager assets = null;
XmlResourceParser parser = null;
try {
- assets = newConfiguredAssetManager();
- int cookie = fd != null
- ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath);
- if (cookie == 0) {
+ final ApkAssets apkAssets;
+ try {
+ apkAssets = fd != null
+ ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ : ApkAssets.loadFromPath(apkPath);
+ } catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
- final DisplayMetrics metrics = new DisplayMetrics();
- metrics.setToDefaults();
-
- parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
@@ -1634,7 +1620,7 @@
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- IoUtils.closeQuietly(assets);
+ // TODO(b/72056911): Implement and call close() on ApkAssets.
}
}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 99eb470..9e3a8f4 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,13 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -26,6 +29,8 @@
import libcore.io.IoUtils;
+import java.io.IOException;
+
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -33,68 +38,66 @@
public class DefaultSplitAssetLoader implements SplitAssetLoader {
private final String mBaseCodePath;
private final String[] mSplitCodePaths;
- private final int mFlags;
-
+ private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
- public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
+ public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
mBaseCodePath = pkg.baseCodePath;
mSplitCodePaths = pkg.splitCodePaths;
mFlags = flags;
}
- private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
- throws PackageParser.PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + apkPath);
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
}
- if (assets.addAssetPath(apkPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
}
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
if (mCachedAssetManager != null) {
return mCachedAssetManager;
}
- AssetManager assets = new AssetManager();
- try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+ ? mSplitCodePaths.length : 0) + 1];
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
- loadApkIntoAssetManager(assets, apkPath, mFlags);
- }
- }
+ // Load the base.
+ int splitIdx = 0;
+ apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
- mCachedAssetManager = assets;
- assets = null;
- return mCachedAssetManager;
- } finally {
- if (assets != null) {
- IoUtils.closeQuietly(assets);
+ // Load any splits.
+ if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+ for (String apkPath : mSplitCodePaths) {
+ apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
}
}
+
+ AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+ mCachedAssetManager = assets;
+ return mCachedAssetManager;
}
@Override
- public AssetManager getSplitAssetManager(int splitIdx)
- throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
return getBaseAssetManager();
}
@Override
public void close() throws Exception {
- if (mCachedAssetManager != null) {
- IoUtils.closeQuietly(mCachedAssetManager);
- }
+ IoUtils.closeQuietly(mCachedAssetManager);
}
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 16023f0..58eaabf 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,17 +15,21 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.SparseArray;
import libcore.io.IoUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,17 +38,15 @@
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader
- extends SplitDependencyLoader<PackageParser.PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
implements SplitAssetLoader {
private final String[] mSplitPaths;
- private final int mFlags;
-
- private String[][] mCachedPaths;
- private AssetManager[] mCachedAssetManagers;
+ private final @ParseFlags int mFlags;
+ private final ApkAssets[][] mCachedSplitApks;
+ private final AssetManager[] mCachedAssetManagers;
public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
- SparseArray<int[]> dependencies, int flags) {
+ SparseArray<int[]> dependencies, @ParseFlags int flags) {
super(dependencies);
// The base is inserted into index 0, so we need to shift all the splits by 1.
@@ -53,7 +55,7 @@
System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
mFlags = flags;
- mCachedPaths = new String[mSplitPaths.length][];
+ mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
mCachedAssetManagers = new AssetManager[mSplitPaths.length];
}
@@ -62,58 +64,60 @@
return mCachedAssetManagers[splitIdx] != null;
}
- private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
- throws PackageParser.PackageParserException {
- final AssetManager assets = new AssetManager();
- try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
-
- for (String assetPath : assetPaths) {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
- !PackageParser.isApkPath(assetPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + assetPath);
- }
-
- if (assets.addAssetPath(assetPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + assetPath);
- }
- }
- return assets;
- } catch (Throwable e) {
- IoUtils.closeQuietly(assets);
- throw e;
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
}
+
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
+ }
+ }
+
+ private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+ final AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+ return assets;
}
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
- int parentSplitIdx) throws PackageParser.PackageParserException {
- final ArrayList<String> assetPaths = new ArrayList<>();
+ int parentSplitIdx) throws PackageParserException {
+ final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+ // Include parent ApkAssets.
if (parentSplitIdx >= 0) {
- Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
+ Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
}
- assetPaths.add(mSplitPaths[splitIdx]);
+ // Include this ApkAssets.
+ assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+ // Load and include all config splits for this feature.
for (int configSplitIdx : configSplitIndices) {
- assetPaths.add(mSplitPaths[configSplitIdx]);
+ assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
}
- mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
- mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
- mFlags);
+
+ // Cache the results.
+ mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
loadDependenciesForSplit(0);
return mCachedAssetManagers[0];
}
@Override
- public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
// Since we insert the base at position 0, and PackageParser keeps splits separate from
// the base, we need to adjust the index.
loadDependenciesForSplit(idx + 1);
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
new file mode 100644
index 0000000..9de8be3
--- /dev/null
+++ b/core/java/android/content/res/ApkAssets.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.res;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * The loaded, immutable, in-memory representation of an APK.
+ *
+ * The main implementation is native C++ and there is very little API surface exposed here. The APK
+ * is mainly accessed via {@link AssetManager}.
+ *
+ * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
+ * making the creation of AssetManagers very cheap.
+ * @hide
+ */
+public final class ApkAssets {
+ @GuardedBy("this") private final long mNativePtr;
+ @GuardedBy("this") private StringBlock mStringBlock;
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk.
+ *
+ * @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 loadFromPath(@NonNull String path) throws IOException {
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk.
+ *
+ * @param path The path to an APK on disk.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @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, boolean system)
+ throws IOException {
+ return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path 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 forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+ * loaded as a shared library.
+ * @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, boolean system,
+ boolean forceSharedLibrary) throws IOException {
+ return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
+ *
+ * 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).
+ *
+ * @param fd The FileDescriptor of an open, readable APK.
+ * @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 forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+ * loaded as a shared library.
+ * @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, boolean system, boolean forceSharedLibrary)
+ throws IOException {
+ return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
+ * is encoded within the IDMAP.
+ *
+ * @param idmapPath Path to the IDMAP of an overlay APK.
+ * @param system When true, the APK is loaded as a system APK (framework).
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
+ throws IOException {
+ return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
+ }
+
+ private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ }
+
+ private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
+ boolean forceSharedLib) throws IOException {
+ Preconditions.checkNotNull(fd, "fd");
+ Preconditions.checkNotNull(friendlyName, "friendlyName");
+ mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
+ mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+ }
+
+ public @NonNull String getAssetPath() {
+ synchronized (this) {
+ return nativeGetAssetPath(mNativePtr);
+ }
+ }
+
+ CharSequence getStringFromPool(int idx) {
+ synchronized (this) {
+ return mStringBlock.get(idx);
+ }
+ }
+
+ /**
+ * 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
+ * dynamically assigned runtime package IDs.
+ *
+ * @param fileName The path to the file within the APK.
+ * @return An XmlResourceParser.
+ * @throws IOException if the file was not found or an error occurred retrieving it.
+ */
+ public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
+ synchronized (this) {
+ long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
+ try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
+ XmlResourceParser parser = block.newParser();
+ // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
+ // which makes newParser always return non-null. But let's be paranoid.
+ if (parser == null) {
+ throw new AssertionError("block.newParser() returned a null parser");
+ }
+ return parser;
+ }
+ }
+ }
+
+ /**
+ * Returns false if the underlying APK was changed since this ApkAssets was loaded.
+ */
+ public boolean isUpToDate() {
+ synchronized (this) {
+ return nativeIsUpToDate(mNativePtr);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ApkAssets{path=" + getAssetPath() + "}";
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestroy(mNativePtr);
+ }
+
+ private static native long nativeLoad(
+ @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ throws IOException;
+ private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, boolean system, boolean forceSharedLib)
+ throws IOException;
+ private static native void nativeDestroy(long ptr);
+ private static native @NonNull String nativeGetAssetPath(long ptr);
+ private static native long nativeGetStringBlock(long ptr);
+ private static native boolean nativeIsUpToDate(long ptr);
+ private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 5f8a34d..2895342 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -18,22 +18,33 @@
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.annotation.StyleRes;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
import android.os.ParcelFileDescriptor;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
-import java.io.FileDescriptor;
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.channels.FileLock;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
/**
@@ -44,7 +55,20 @@
* bytes.
*/
public final class AssetManager implements AutoCloseable {
- /* modes used when opening an asset */
+ private static final String TAG = "AssetManager";
+ private static final boolean DEBUG_REFS = false;
+
+ private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
+
+ private static final Object sSync = new Object();
+
+ private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
+
+ // Not private for LayoutLib's BridgeAssetManager.
+ @GuardedBy("sSync") static AssetManager sSystem = null;
+
+ @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
+ @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
/**
* Mode for {@link #open(String, int)}: no specific information about how
@@ -67,146 +91,358 @@
*/
public static final int ACCESS_BUFFER = 3;
- private static final String TAG = "AssetManager";
- private static final boolean localLOGV = false || false;
-
- private static final boolean DEBUG_REFS = false;
-
- private static final Object sSync = new Object();
- /*package*/ static AssetManager sSystem = null;
+ @GuardedBy("this") private final TypedValue mValue = new TypedValue();
+ @GuardedBy("this") private final long[] mOffsets = new long[2];
- private final TypedValue mValue = new TypedValue();
- private final long[] mOffsets = new long[2];
-
- // For communication with native code.
- private long mObject;
+ // Pointer to native implementation, stuffed inside a long.
+ @GuardedBy("this") private long mObject;
- private StringBlock mStringBlocks[] = null;
-
- private int mNumRefs = 1;
- private boolean mOpen = true;
- private HashMap<Long, RuntimeException> mRefStacks;
-
+ // The loaded asset paths.
+ @GuardedBy("this") private ApkAssets[] mApkAssets;
+
+ // Debug/reference counting implementation.
+ @GuardedBy("this") private boolean mOpen = true;
+ @GuardedBy("this") private int mNumRefs = 1;
+ @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+
+ /**
+ * A Builder class that helps create an AssetManager with only a single invocation of
+ * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
+ * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined
+ * with the user's call to add additional ApkAssets, results in multiple calls to
+ * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}.
+ * @hide
+ */
+ public static class Builder {
+ private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
+
+ public Builder addApkAssets(ApkAssets apkAssets) {
+ mUserApkAssets.add(apkAssets);
+ return this;
+ }
+
+ public AssetManager build() {
+ // Retrieving the system ApkAssets forces their creation as well.
+ final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
+
+ final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size();
+ final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
+
+ System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
+
+ final int userApkAssetCount = mUserApkAssets.size();
+ for (int i = 0; i < userApkAssetCount; i++) {
+ apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
+ }
+
+ // Calling this constructor prevents creation of system ApkAssets, which we took care
+ // of in this Builder.
+ final AssetManager assetManager = new AssetManager(false /*sentinel*/);
+ assetManager.mApkAssets = apkAssets;
+ AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
+ false /*invalidateCaches*/);
+ return assetManager;
+ }
+ }
+
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
- * {@hide}
+ * @hide
*/
public AssetManager() {
- synchronized (this) {
- if (DEBUG_REFS) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
- }
- init(false);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
- ensureSystemAssets();
+ final ApkAssets[] assets;
+ synchronized (sSync) {
+ createSystemAssetsInZygoteLocked();
+ assets = sSystemApkAssets;
+ }
+
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
+ }
+
+ // Always set the framework resources.
+ setApkAssets(assets, false /*invalidateCaches*/);
+ }
+
+ /**
+ * Private constructor that doesn't call ensureSystemAssets.
+ * Used for the creation of system assets.
+ */
+ @SuppressWarnings("unused")
+ private AssetManager(boolean sentinel) {
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
}
}
- private static void ensureSystemAssets() {
- synchronized (sSync) {
- if (sSystem == null) {
- AssetManager system = new AssetManager(true);
- system.makeStringBlocks(null);
- sSystem = system;
- }
+ /**
+ * This must be called from Zygote so that system assets are shared by all applications.
+ */
+ @GuardedBy("sSync")
+ private static void createSystemAssetsInZygoteLocked() {
+ if (sSystem != null) {
+ return;
+ }
+
+ // Make sure that all IDMAPs are up to date.
+ nativeVerifySystemIdmaps();
+
+ try {
+ final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
+ loadStaticRuntimeOverlays(apkAssets);
+
+ sSystemApkAssetsSet = new ArraySet<>(apkAssets);
+ sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+ sSystem = new AssetManager(true /*sentinel*/);
+ sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to create system AssetManager", e);
}
}
-
- private AssetManager(boolean isSystem) {
- if (DEBUG_REFS) {
- synchronized (this) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
- }
+
+ /**
+ * Loads the static runtime overlays declared in /data/resource-cache/overlays.list.
+ * Throws an exception if the file is corrupt or if loading the APKs referenced by the file
+ * fails. Returns quietly if the overlays.list file doesn't exist.
+ * @param outApkAssets The list to fill with the loaded ApkAssets.
+ */
+ private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)
+ throws IOException {
+ final FileInputStream fis;
+ try {
+ fis = new FileInputStream("/data/resource-cache/overlays.list");
+ } catch (FileNotFoundException e) {
+ // We might not have any overlays, this is fine. We catch here since ApkAssets
+ // loading can also fail with the same exception, which we would want to propagate.
+ Log.i(TAG, "no overlays.list file found");
+ return;
}
- init(true);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+
+ try {
+ // Acquire a lock so that any idmap scanning doesn't impact the current set.
+ // The order of this try-with-resources block matters. We must release the lock, and
+ // then close the file streams when exiting the block.
+ try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) {
+ for (String line; (line = br.readLine()) != null; ) {
+ final String idmapPath = line.split(" ")[1];
+ outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
+ }
+ } finally {
+ // When BufferedReader is closed above, FileInputStream is closed as well. But let's be
+ // paranoid.
+ IoUtils.closeQuietly(fis);
+ }
}
/**
* Return a global shared asset manager that provides access to only
* system assets (no application assets).
- * {@hide}
+ * @hide
*/
public static AssetManager getSystem() {
- ensureSystemAssets();
- return sSystem;
+ synchronized (sSync) {
+ createSystemAssetsInZygoteLocked();
+ return sSystem;
+ }
}
/**
* Close this asset manager.
*/
+ @Override
public void close() {
- synchronized(this) {
- //System.out.println("Release: num=" + mNumRefs
- // + ", released=" + mReleased);
+ synchronized (this) {
+ if (!mOpen) {
+ return;
+ }
+
+ mOpen = false;
+ decRefsLocked(hashCode());
+ }
+ }
+
+ /**
+ * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
+ * family of methods.
+ *
+ * @param apkAssets The new set of paths.
+ * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
+ * Set this to false if you are appending new resources
+ * (not new configurations).
+ * @hide
+ */
+ public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
+ Preconditions.checkNotNull(apkAssets, "apkAssets");
+
+ ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length];
+
+ // Copy the system assets first.
+ System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
+
+ // Copy the given ApkAssets if they are not already in the system list.
+ int newLength = sSystemApkAssets.length;
+ for (ApkAssets apkAsset : apkAssets) {
+ if (!sSystemApkAssetsSet.contains(apkAsset)) {
+ newApkAssets[newLength++] = apkAsset;
+ }
+ }
+
+ // Truncate if necessary.
+ if (newLength != newApkAssets.length) {
+ newApkAssets = Arrays.copyOf(newApkAssets, newLength);
+ }
+
+ synchronized (this) {
+ ensureOpenLocked();
+ mApkAssets = newApkAssets;
+ nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+ if (invalidateCaches) {
+ // Invalidate all caches.
+ invalidateCachesLocked(-1);
+ }
+ }
+ }
+
+ /**
+ * Invalidates the caches in this AssetManager according to the bitmask `diff`.
+ *
+ * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
+ * @see ActivityInfo.Config
+ */
+ private void invalidateCachesLocked(int diff) {
+ // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
+ }
+
+ /**
+ * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
+ * returns a 0-length array.
+ * @hide
+ */
+ public @NonNull ApkAssets[] getApkAssets() {
+ synchronized (this) {
if (mOpen) {
- mOpen = false;
- decRefsLocked(this.hashCode());
+ return mApkAssets;
}
}
+ return sEmptyApkAssets;
}
/**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @return the string value, or {@code null}
+ * Returns a cookie for use with the other APIs of AssetManager.
+ * @return 0 if the path was not found, otherwise a positive integer cookie representing
+ * this path in the AssetManager.
+ * @hide
*/
- @Nullable
- final CharSequence getResourceText(@StringRes int resId) {
+ public int findCookieForPath(@NonNull String path) {
+ Preconditions.checkNotNull(path, "path");
synchronized (this) {
- final TypedValue outValue = mValue;
- if (getResourceValue(resId, 0, outValue, true)) {
- return outValue.coerceToString();
+ ensureValidLocked();
+ final int count = mApkAssets.length;
+ for (int i = 0; i < count; i++) {
+ if (path.equals(mApkAssets[i].getAssetPath())) {
+ return i + 1;
+ }
}
- return null;
}
+ return 0;
}
/**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @param bagEntryId
- * @return the string value, or {@code null}
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
*/
- @Nullable
- final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ @Deprecated
+ public int addAssetPath(String path) {
+ return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+ }
+
+ /**
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
+ */
+ @Deprecated
+ public int addAssetPathAsSharedLibrary(String path) {
+ return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+ }
+
+ /**
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
+ */
+ @Deprecated
+ public int addOverlayPath(String path) {
+ return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+ }
+
+ private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
+ Preconditions.checkNotNull(path, "path");
synchronized (this) {
- final TypedValue outValue = mValue;
- final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
- if (block < 0) {
- return null;
+ 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;
+ }
}
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- return mStringBlocks[block].get(outValue.data);
+ final ApkAssets assets;
+ try {
+ if (overlay) {
+ // TODO(b/70343104): This hardcoded path will be removed once
+ // addAssetPathInternal is deleted.
+ final String idmapPath = "/data/resource-cache/"
+ + path.substring(1).replace('/', '@')
+ + "@idmap";
+ assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
+ } else {
+ assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
+ }
+ } catch (IOException e) {
+ return 0;
}
- return outValue.coerceToString();
+
+ mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
+ mApkAssets[count] = assets;
+ nativeSetApkAssets(mObject, mApkAssets, true);
+ invalidateCachesLocked(-1);
+ return count + 1;
}
}
/**
- * Retrieves the string array associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier of the string array
- * @return the string array, or {@code null}
+ * Ensures that the native implementation has not been destroyed.
+ * The AssetManager may have been closed, but references to it still exist
+ * and therefore the native implementation is not destroyed.
*/
- @Nullable
- final String[] getResourceStringArray(@ArrayRes int resId) {
- return getArrayStringResource(resId);
+ @GuardedBy("this")
+ private void ensureValidLocked() {
+ if (mObject == 0) {
+ throw new RuntimeException("AssetManager has been destroyed");
+ }
+ }
+
+ /**
+ * Ensures that the AssetManager has not been explicitly closed. If this method passes,
+ * then this implies that ensureValidLocked() also passes.
+ */
+ @GuardedBy("this")
+ private void ensureOpenLocked() {
+ // If mOpen is true, this implies that mObject != 0.
+ if (!mOpen) {
+ throw new RuntimeException("AssetManager has been closed");
+ }
}
/**
@@ -221,11 +457,14 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+ boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
- if (block < 0) {
+ ensureValidLocked();
+ final int cookie = nativeGetResourceValue(
+ mObject, resId, (short) densityDpi, outValue, resolveRefs);
+ if (cookie <= 0) {
return false;
}
@@ -234,38 +473,156 @@
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mStringBlocks[block].get(outValue.data);
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
return true;
}
}
/**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceText(@StringRes int resId) {
+ synchronized (this) {
+ final TypedValue outValue = mValue;
+ if (getResourceValue(resId, 0, outValue, true)) {
+ return outValue.coerceToString();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @param bagEntryId the index into the bag to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ synchronized (this) {
+ ensureValidLocked();
+ final TypedValue outValue = mValue;
+ final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+ if (cookie <= 0) {
+ return null;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return outValue.coerceToString();
+ }
+ }
+
+ int getResourceArraySize(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArraySize(mObject, resId);
+ }
+ }
+
+ /**
+ * Populates `outData` with array elements of `resId`. `outData` is normally
+ * used with
+ * {@link TypedArray}.
+ *
+ * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
+ * long,
+ * with the indices of the data representing the type, value, asset cookie,
+ * resource ID,
+ * configuration change mask, and density of the element.
+ *
+ * @param resId The resource ID of an array resource.
+ * @param outData The array to populate with data.
+ * @return The length of the array.
+ *
+ * @see TypedArray#STYLE_TYPE
+ * @see TypedArray#STYLE_DATA
+ * @see TypedArray#STYLE_ASSET_COOKIE
+ * @see TypedArray#STYLE_RESOURCE_ID
+ * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
+ * @see TypedArray#STYLE_DENSITY
+ */
+ int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+ Preconditions.checkNotNull(outData, "outData");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArray(mObject, resId, outData);
+ }
+ }
+
+ /**
+ * Retrieves the string array associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier of the string array
+ * @return the string array, or {@code null}
+ */
+ @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceStringArray(mObject, resId);
+ }
+ }
+
+ /**
* Retrieve the text array associated with a particular resource
* identifier.
*
* @param resId the resource id of the string array
*/
- final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+ @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
synchronized (this) {
- final int[] rawInfoArray = getArrayStringInfo(resId);
+ ensureValidLocked();
+ final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
if (rawInfoArray == null) {
return null;
}
+
final int rawInfoArrayLen = rawInfoArray.length;
final int infoArrayLen = rawInfoArrayLen / 2;
- int block;
- int index;
final CharSequence[] retArray = new CharSequence[infoArrayLen];
for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
- block = rawInfoArray[i];
- index = rawInfoArray[i + 1];
- retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ int cookie = rawInfoArray[i];
+ int index = rawInfoArray[i + 1];
+ retArray[j] = (index >= 0 && cookie > 0)
+ ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
}
return retArray;
}
}
+ @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceIntArray(mObject, resId);
+ }
+ }
+
+ /**
+ * Get the attributes for a style resource. These are the <item>
+ * elements in
+ * a <style> resource.
+ * @param resId The resource ID of the style
+ * @return An array of attribute IDs.
+ */
+ @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetStyleAttributes(mObject, resId);
+ }
+ }
+
/**
* Populates {@code outValue} with the data associated with a particular
* resource identifier for the current configuration. Resolves theme
@@ -279,73 +636,88 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+ boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
boolean resolveRefs) {
- final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
- if (block < 0) {
- return false;
- }
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- final StringBlock[] blocks = ensureStringBlocks();
- outValue.string = blocks[block].get(outValue.data);
- }
- return true;
- }
-
- /**
- * Ensures the string blocks are loaded.
- *
- * @return the string blocks
- */
- @NonNull
- final StringBlock[] ensureStringBlocks() {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- if (mStringBlocks == null) {
- makeStringBlocks(sSystem.mStringBlocks);
+ ensureValidLocked();
+ final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+ resolveRefs);
+ if (cookie <= 0) {
+ return false;
}
- return mStringBlocks;
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return true;
}
}
- /*package*/ final void makeStringBlocks(StringBlock[] seed) {
- final int seedNum = (seed != null) ? seed.length : 0;
- final int num = getStringBlockCount();
- mStringBlocks = new StringBlock[num];
- if (localLOGV) Log.v(TAG, "Making string blocks for " + this
- + ": " + num);
- for (int i=0; i<num; i++) {
- if (i < seedNum) {
- mStringBlocks[i] = seed[i];
- } else {
- mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
- }
- }
- }
-
- /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
+ void dumpTheme(long theme, int priority, String tag, String prefix) {
synchronized (this) {
- // Cookies map to string blocks starting at 1.
- return mStringBlocks[cookie - 1].get(id);
+ ensureValidLocked();
+ nativeThemeDump(mObject, theme, priority, tag, prefix);
}
}
+ @Nullable String getResourceName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourcePackageName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourcePackageName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceTypeName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceTypeName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceEntryName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceEntryName(mObject, resId);
+ }
+ }
+
+ @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+ @Nullable String defPackage) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+ }
+ }
+
+ CharSequence getPooledStringForCookie(int cookie, int id) {
+ // Cookies map to ApkAssets starting at 1.
+ return getApkAssets()[cookie - 1].getStringFromPool(id);
+ }
+
/**
* Open an asset using ACCESS_STREAMING mode. This provides access to
* files that have been bundled with an application as assets -- that is,
* files placed in to the "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
*
* @see #open(String, int)
* @see #list
*/
- public final InputStream open(String fileName) throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName) throws IOException {
return open(fileName, ACCESS_STREAMING);
}
@@ -355,8 +727,7 @@
* with an application as assets -- that is, files placed in to the
* "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
* @param accessMode Desired access mode for retrieving the data.
*
* @see #ACCESS_UNKNOWN
@@ -366,34 +737,40 @@
* @see #open(String)
* @see #list
*/
- public final InputStream open(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- long asset = openAsset(fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
- public final AssetFileDescriptor openFd(String fileName)
- throws IOException {
+ /**
+ * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides access to files that have been bundled with an application as assets -- that
+ * is, files placed in to the "assets" directory.
+ *
+ * The asset must be uncompressed, or an exception will be thrown.
+ *
+ * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @return An open AssetFileDescriptor.
+ */
+ public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
/**
@@ -408,90 +785,121 @@
*
* @see #open
*/
- public native final String[] list(String path)
- throws IOException;
+ public @Nullable String[] list(@NonNull String path) throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeList(mObject, path);
+ }
+ }
/**
- * {@hide}
* Open a non-asset file as an asset using ACCESS_STREAMING mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ *
* @see #open(String)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName) throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
return openNonAsset(0, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset file as an asset using a specific access mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ * @param accessMode Desired access mode for retrieving the data.
+ *
+ * @see #ACCESS_UNKNOWN
+ * @see #ACCESS_STREAMING
+ * @see #ACCESS_RANDOM
+ * @see #ACCESS_BUFFER
* @see #open(String, int)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+ throws IOException {
return openNonAsset(0, fileName, accessMode);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+ throws IOException {
return openNonAsset(cookie, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
* @param accessMode Desired access mode for retrieving the data.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- long asset = openNonAssetNative(cookie, fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- public final AssetFileDescriptor openNonAssetFd(String fileName)
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
throws IOException {
return openNonAssetFd(0, fileName);
}
-
- public final AssetFileDescriptor openNonAssetFd(int cookie,
- String fileName) throws IOException {
+
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd =
+ nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
- fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
/**
@@ -499,7 +907,7 @@
*
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(String fileName)
+ public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
throws IOException {
return openXmlResourceParser(0, fileName);
}
@@ -510,270 +918,265 @@
* @param cookie Identifier of the package to be opened.
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(int cookie,
- String fileName) throws IOException {
- XmlBlock block = openXmlBlockAsset(cookie, fileName);
- XmlResourceParser rp = block.newParser();
- block.close();
- return rp;
+ public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+ throws IOException {
+ try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+ XmlResourceParser parser = block.newParser();
+ // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+ // a valid native pointer, which makes newParser always return non-null. But let's
+ // be paranoid.
+ if (parser == null) {
+ throw new AssertionError("block.newParser() returned a null parser");
+ }
+ return parser;
+ }
}
/**
- * {@hide}
- * Retrieve a non-asset as a compiled XML file. Not for use by
- * applications.
+ * Retrieve a non-asset as a compiled XML file. Not for use by applications.
*
* @param fileName The name of the file to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
return openXmlBlockAsset(0, fileName);
}
/**
- * {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
* applications.
*
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ if (xmlBlock == 0) {
+ throw new FileNotFoundException("Asset XML file: " + fileName);
}
- long xmlBlock = openXmlAssetNative(cookie, fileName);
- if (xmlBlock != 0) {
- XmlBlock res = new XmlBlock(this, xmlBlock);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final XmlBlock block = new XmlBlock(this, xmlBlock);
+ incRefsLocked(block.hashCode());
+ return block;
}
- throw new FileNotFoundException("Asset XML file: " + fileName);
}
- /*package*/ void xmlBlockGone(int id) {
+ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
- /*package*/ final long createTheme() {
+ void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+ long outIndicesAddress) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long res = newTheme();
- incRefsLocked(res);
- return res;
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+ outIndicesAddress);
}
}
- /*package*/ final void releaseTheme(long theme) {
+ boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+ @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
synchronized (this) {
- deleteTheme(theme);
- decRefsLocked(theme);
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeResolveAttrs(mObject,
+ themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
}
}
+ boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+ @NonNull int[] outValues, @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(parser, "parser");
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeRetrieveAttributes(
+ mObject, parser.mParseState, inAttrs, outValues, outIndices);
+ }
+ }
+
+ long createTheme() {
+ synchronized (this) {
+ ensureValidLocked();
+ long themePtr = nativeThemeCreate(mObject);
+ incRefsLocked(themePtr);
+ return themePtr;
+ }
+ }
+
+ void releaseTheme(long themePtr) {
+ synchronized (this) {
+ nativeThemeDestroy(themePtr);
+ decRefsLocked(themePtr);
+ }
+ }
+
+ void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeThemeApplyStyle(mObject, themePtr, resId, force);
+ }
+ }
+
+ @Override
protected void finalize() throws Throwable {
- try {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this
- + " finalized with non-zero refs: " + mNumRefs);
- if (mRefStacks != null) {
- for (RuntimeException e : mRefStacks.values()) {
- Log.w(TAG, "Reference from here", e);
- }
+ if (DEBUG_REFS && mNumRefs != 0) {
+ Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+ if (mRefStacks != null) {
+ for (RuntimeException e : mRefStacks.values()) {
+ Log.w(TAG, "Reference from here", e);
}
}
- destroy();
- } finally {
- super.finalize();
+ }
+
+ if (mObject != 0) {
+ nativeDestroy(mObject);
}
}
-
+
+ /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+ safe and it does not rely on AssetManager once it has been created. It completely owns the
+ underlying Asset. */
public final class AssetInputStream extends InputStream {
+ private long mAssetNativePtr;
+ private long mLength;
+ private long mMarkPos;
+
/**
* @hide
*/
public final int getAssetInt() {
throw new UnsupportedOperationException();
}
+
/**
* @hide
*/
public final long getNativeAsset() {
- return mAsset;
+ return mAssetNativePtr;
}
- private AssetInputStream(long asset)
- {
- mAsset = asset;
- mLength = getAssetLength(asset);
+
+ private AssetInputStream(long assetNativePtr) {
+ mAssetNativePtr = assetNativePtr;
+ mLength = nativeAssetGetLength(assetNativePtr);
}
+
+ @Override
public final int read() throws IOException {
- return readAssetChar(mAsset);
+ ensureOpen();
+ return nativeAssetReadChar(mAssetNativePtr);
}
- public final boolean markSupported() {
- return true;
+
+ @Override
+ public final int read(@NonNull byte[] b) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
}
- public final int available() throws IOException {
- long len = getAssetRemainingLength(mAsset);
- return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
+
+ @Override
+ public final int read(@NonNull byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, off, len);
}
- public final void close() throws IOException {
- synchronized (AssetManager.this) {
- if (mAsset != 0) {
- destroyAsset(mAsset);
- mAsset = 0;
- decRefsLocked(hashCode());
- }
- }
- }
- public final void mark(int readlimit) {
- mMarkPos = seekAsset(mAsset, 0, 0);
- }
- public final void reset() throws IOException {
- seekAsset(mAsset, mMarkPos, -1);
- }
- public final int read(byte[] b) throws IOException {
- return readAsset(mAsset, b, 0, b.length);
- }
- public final int read(byte[] b, int off, int len) throws IOException {
- return readAsset(mAsset, b, off, len);
- }
+
+ @Override
public final long skip(long n) throws IOException {
- long pos = seekAsset(mAsset, 0, 0);
- if ((pos+n) > mLength) {
- n = mLength-pos;
+ ensureOpen();
+ long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ if ((pos + n) > mLength) {
+ n = mLength - pos;
}
if (n > 0) {
- seekAsset(mAsset, n, 0);
+ nativeAssetSeek(mAssetNativePtr, n, 0);
}
return n;
}
- protected void finalize() throws Throwable
- {
+ @Override
+ public final int available() throws IOException {
+ ensureOpen();
+ final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
+ }
+
+ @Override
+ public final boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public final void mark(int readlimit) {
+ ensureOpen();
+ mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ }
+
+ @Override
+ public final void reset() throws IOException {
+ ensureOpen();
+ nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
+ }
+
+ @Override
+ public final void close() throws IOException {
+ if (mAssetNativePtr != 0) {
+ nativeAssetDestroy(mAssetNativePtr);
+ mAssetNativePtr = 0;
+
+ synchronized (AssetManager.this) {
+ decRefsLocked(hashCode());
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
close();
}
- private long mAsset;
- private long mLength;
- private long mMarkPos;
- }
-
- /**
- * Add an additional set of assets to the asset manager. This can be
- * either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPath(String path) {
- return addAssetPathInternal(path, false);
- }
-
- /**
- * Add an application assets to the asset manager and loading it as shared library.
- * This can be either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, true);
- }
-
- private final int addAssetPathInternal(String path, boolean appAsLib) {
- synchronized (this) {
- int res = addAssetPathNative(path, appAsLib);
- makeStringBlocks(mStringBlocks);
- return res;
+ private void ensureOpen() {
+ if (mAssetNativePtr == 0) {
+ throw new IllegalStateException("AssetInputStream is closed");
+ }
}
}
- private native final int addAssetPathNative(String path, boolean appAsLib);
-
- /**
- * Add an additional set of assets to the asset manager from an already open
- * FileDescriptor. Not for use by applications.
- * This does not give full AssetManager functionality for these assets,
- * since the origin of the file is not known for purposes of sharing,
- * overlay resolution, and other features. However it does allow you
- * to do simple access to the contents of the given fd as an apk file.
- * 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).
- * Returns the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public int addAssetFd(FileDescriptor fd, String debugPathName) {
- return addAssetFdInternal(fd, debugPathName, false);
- }
-
- private int addAssetFdInternal(FileDescriptor fd, String debugPathName,
- boolean appAsLib) {
- synchronized (this) {
- int res = addAssetFdNative(fd, debugPathName, appAsLib);
- makeStringBlocks(mStringBlocks);
- return res;
- }
- }
-
- private native int addAssetFdNative(FileDescriptor fd, String debugPathName,
- boolean appAsLib);
-
- /**
- * Add a set of assets to overlay an already added set of assets.
- *
- * This is only intended for application resources. System wide resources
- * are handled before any Java code is executed.
- *
- * {@hide}
- */
-
- public final int addOverlayPath(String idmapPath) {
- synchronized (this) {
- int res = addOverlayPathNative(idmapPath);
- makeStringBlocks(mStringBlocks);
- return res;
- }
- }
-
- /**
- * See addOverlayPath.
- *
- * {@hide}
- */
- public native final int addOverlayPathNative(String idmapPath);
-
- /**
- * Add multiple sets of assets to the asset manager at once. See
- * {@link #addAssetPath(String)} for more information. Returns array of
- * cookies for each added asset with 0 indicating failure, or null if
- * the input array of paths is null.
- * {@hide}
- */
- public final int[] addAssetPaths(String[] paths) {
- if (paths == null) {
- return null;
- }
-
- int[] cookies = new int[paths.length];
- for (int i = 0; i < paths.length; i++) {
- cookies[i] = addAssetPath(paths[i]);
- }
-
- return cookies;
- }
-
/**
* Determine whether the state in this asset manager is up-to-date with
* the files on the filesystem. If false is returned, you need to
* instantiate a new AssetManager class to see the new data.
- * {@hide}
+ * @hide
*/
- public native final boolean isUpToDate();
+ public boolean isUpToDate() {
+ for (ApkAssets apkAssets : getApkAssets()) {
+ if (!apkAssets.isUpToDate()) {
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Get the locales that this asset manager contains data for.
@@ -786,7 +1189,12 @@
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
* and {@code CC} is a two letter country code.
*/
- public native final String[] getLocales();
+ public String[] getLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, false /*excludeSystem*/);
+ }
+ }
/**
* Same as getLocales(), except that locales that are only provided by the system (i.e. those
@@ -796,132 +1204,58 @@
* assets support Cherokee and French, getLocales() would return
* [Cherokee, English, French, German], while getNonSystemLocales() would return
* [Cherokee, French].
- * {@hide}
+ * @hide
*/
- public native final String[] getNonSystemLocales();
-
- /** {@hide} */
- public native final Configuration[] getSizeConfigurations();
+ public String[] getNonSystemLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, true /*excludeSystem*/);
+ }
+ }
/**
- * Change the configuation used when retrieving resources. Not for use by
+ * @hide
+ */
+ Configuration[] getSizeConfigurations() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetSizeConfigurations(mObject);
+ }
+ }
+
+ /**
+ * Change the configuration used when retrieving resources. Not for use by
* applications.
- * {@hide}
+ * @hide
*/
- public native final void setConfiguration(int mcc, int mnc, String locale,
- int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
- int screenLayout, int uiMode, int colorMode, int majorVersion);
+ public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
+ int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+ int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+ int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+ keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+ smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+ colorMode, majorVersion);
+ }
+ }
/**
- * Retrieve the resource identifier for the given resource name.
+ * @hide
*/
- /*package*/ native final int getResourceIdentifier(String name,
- String defType,
- String defPackage);
-
- /*package*/ native final String getResourceName(int resid);
- /*package*/ native final String getResourcePackageName(int resid);
- /*package*/ native final String getResourceTypeName(int resid);
- /*package*/ native final String getResourceEntryName(int resid);
-
- private native final long openAsset(String fileName, int accessMode);
- private final native ParcelFileDescriptor openAssetFd(String fileName,
- long[] outOffsets) throws IOException;
- private native final long openNonAssetNative(int cookie, String fileName,
- int accessMode);
- private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
- String fileName, long[] outOffsets) throws IOException;
- private native final void destroyAsset(long asset);
- private native final int readAssetChar(long asset);
- private native final int readAsset(long asset, byte[] b, int off, int len);
- private native final long seekAsset(long asset, long offset, int whence);
- private native final long getAssetLength(long asset);
- private native final long getAssetRemainingLength(long asset);
-
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceValue(int ident, short density, TypedValue outValue,
- boolean resolve);
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
- boolean resolve);
- /*package*/ static final int STYLE_NUM_ENTRIES = 6;
- /*package*/ static final int STYLE_TYPE = 0;
- /*package*/ static final int STYLE_DATA = 1;
- /*package*/ static final int STYLE_ASSET_COOKIE = 2;
- /*package*/ static final int STYLE_RESOURCE_ID = 3;
-
- /* Offset within typed data array for native changingConfigurations. */
- static final int STYLE_CHANGING_CONFIGURATIONS = 4;
-
- /*package*/ static final int STYLE_DENSITY = 5;
- /*package*/ native static final void applyStyle(long theme,
- int defStyleAttr, int defStyleRes, long xmlParser,
- int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress);
- /*package*/ native static final boolean resolveAttrs(long theme,
- int defStyleAttr, int defStyleRes, int[] inValues,
- int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final boolean retrieveAttributes(
- long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final int getArraySize(int resource);
- /*package*/ native final int retrieveArray(int resource, int[] outValues);
- private native final int getStringBlockCount();
- private native final long getNativeStringBlock(int block);
-
- /**
- * {@hide}
- */
- public native final String getCookieName(int cookie);
-
- /**
- * {@hide}
- */
- public native final SparseArray<String> getAssignedPackageIdentifiers();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetCount();
-
- /**
- * {@hide}
- */
- public native static final String getAssetAllocations();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetManagerCount();
-
- private native final long newTheme();
- private native final void deleteTheme(long theme);
- /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
- /*package*/ native static final void copyTheme(long dest, long source);
- /*package*/ native static final void clearTheme(long theme);
- /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
- TypedValue outValue,
- boolean resolve);
- /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
- /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
-
- private native final long openXmlAssetNative(int cookie, String fileName);
-
- private native final String[] getArrayStringResource(int arrayRes);
- private native final int[] getArrayStringInfo(int arrayRes);
- /*package*/ native final int[] getArrayIntResource(int arrayRes);
- /*package*/ native final int[] getStyleAttributes(int themeRes);
-
- private native final void init(boolean isSystem);
- private native final void destroy();
+ public SparseArray<String> getAssignedPackageIdentifiers() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetAssignedPackageIdentifiers(mObject);
+ }
+ }
@GuardedBy("this")
- private final void incRefsLocked(long id) {
+ private void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
- mRefStacks = new HashMap<Long, RuntimeException>();
+ mRefStacks = new HashMap<>();
}
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
@@ -931,15 +1265,117 @@
}
@GuardedBy("this")
- private final void decRefsLocked(long id) {
+ private void decRefsLocked(long id) {
if (DEBUG_REFS && mRefStacks != null) {
mRefStacks.remove(id);
}
mNumRefs--;
- //System.out.println("Dec streams: mNumRefs=" + mNumRefs
- // + " mReleased=" + mReleased);
- if (mNumRefs == 0) {
- destroy();
+ if (mNumRefs == 0 && mObject != 0) {
+ nativeDestroy(mObject);
+ mObject = 0;
+ mApkAssets = sEmptyApkAssets;
}
}
+
+ // AssetManager setup native methods.
+ private static native long nativeCreate();
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
+ boolean invalidateCaches);
+ private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
+ @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
+ int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+ int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
+ int uiMode, int colorMode, int majorVersion);
+ private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
+ long ptr);
+
+ // File native methods.
+ private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
+ throws IOException;
+ private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
+ @NonNull String fileName, long[] outOffsets) throws IOException;
+ private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
+ int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
+ @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
+ private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+
+ // Primitive resource native methods.
+ private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
+ @NonNull TypedValue outValue, boolean resolveReferences);
+ private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
+ @NonNull TypedValue outValue);
+
+ private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
+ @StyleRes int resId);
+ private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
+ @NonNull int[] outValues);
+
+ // Resource name/ID native methods.
+ private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
+ @Nullable String defType, @Nullable String defPackage);
+ private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourcePackageName(long ptr,
+ @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
+ private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
+ private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+
+ // Style attribute retrieval native methods.
+ private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
+ long outValuesAddress, long outIndicesAddress);
+ private static native boolean nativeResolveAttrs(long ptr, long themePtr,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+ private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+
+ // Theme related native methods
+ private static native long nativeThemeCreate(long ptr);
+ private static native void nativeThemeDestroy(long themePtr);
+ private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
+ boolean force);
+ static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+ static native void nativeThemeClear(long themePtr);
+ private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
+ @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
+ private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+ String prefix);
+ static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+
+ // AssetInputStream related native methods.
+ private static native void nativeAssetDestroy(long assetPtr);
+ private static native int nativeAssetReadChar(long assetPtr);
+ private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
+ private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
+ private static native long nativeAssetGetLength(long assetPtr);
+ private static native long nativeAssetGetRemainingLength(long assetPtr);
+
+ private static native void nativeVerifySystemIdmaps();
+
+ // Global debug native methods.
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetCount();
+
+ /**
+ * @hide
+ */
+ public static native String getAssetAllocations();
+
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetManagerCount();
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ad85e71..d813382 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -590,7 +590,7 @@
*/
@NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
if (res != null) {
return res;
}
@@ -613,13 +613,13 @@
@NonNull
public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
final ResourcesImpl impl = mResourcesImpl;
- int len = impl.getAssets().getArraySize(id);
+ int len = impl.getAssets().getResourceArraySize(id);
if (len < 0) {
throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = impl.getAssets().retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().getResourceArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -1794,8 +1794,7 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
- array.mData, array.mIndices);
+ mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
array.mXml = parser;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 424fa83..157910a 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -170,7 +170,6 @@
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
- mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -1300,8 +1299,7 @@
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
+ mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
mKey.append(resId, force);
}
@@ -1310,7 +1308,7 @@
void setTo(ThemeImpl other) {
synchronized (mKey) {
synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
+ AssetManager.nativeThemeCopy(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
mKey.setTo(other.getKey());
@@ -1333,12 +1331,10 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
+ mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+ array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
-
return array;
}
}
@@ -1355,7 +1351,7 @@
}
final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
array.mTheme = wrapper;
array.mXml = null;
return array;
@@ -1375,14 +1371,14 @@
@Config int getChangingConfigurations() {
synchronized (mKey) {
final @NativeConfig int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
+ AssetManager.nativeThemeGetChangingConfigurations(mTheme);
return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
}
}
public void dump(int priority, String tag, String prefix) {
synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ mAssets.dumpTheme(mTheme, priority, tag, prefix);
}
}
@@ -1411,13 +1407,13 @@
*/
void rebase() {
synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
+ AssetManager.nativeThemeClear(mTheme);
// Reapply the same styles in the same order.
for (int i = 0; i < mKey.mCount; i++) {
final int resId = mKey.mResId[i];
final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
+ mAssets.applyStyleToTheme(mTheme, resId, force);
}
}
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f33c751..cbb3c6d 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -61,6 +61,15 @@
return attrs;
}
+ // STYLE_ prefixed constants are offsets within the typed data array.
+ static final int STYLE_NUM_ENTRIES = 6;
+ static final int STYLE_TYPE = 0;
+ static final int STYLE_DATA = 1;
+ static final int STYLE_ASSET_COOKIE = 2;
+ static final int STYLE_RESOURCE_ID = 3;
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+ static final int STYLE_DENSITY = 5;
+
private final Resources mResources;
private DisplayMetrics mMetrics;
private AssetManager mAssets;
@@ -78,7 +87,7 @@
private void resize(int len) {
mLength = len;
- final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ final int dataLen = len * STYLE_NUM_ENTRIES;
final int indicesLen = len + 1;
final VMRuntime runtime = VMRuntime.getRuntime();
if (mDataAddress == 0 || mData.length < dataLen) {
@@ -166,9 +175,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -203,9 +212,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -242,14 +251,13 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_STRING) {
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]).toString();
+ return mXml.getPooledString(data[index + STYLE_DATA]).toString();
}
}
return null;
@@ -274,11 +282,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
@@ -320,14 +328,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA] != 0;
+ return data[index + STYLE_DATA] != 0;
}
final TypedValue v = mValue;
@@ -359,14 +367,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -396,16 +404,16 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FLOAT) {
- return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+ return Float.intBitsToFloat(data[index + STYLE_DATA]);
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -446,15 +454,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_STRING) {
final TypedValue value = mValue;
if (getValueAt(index, value)) {
@@ -498,7 +506,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -533,7 +541,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -564,15 +572,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -612,15 +620,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -661,15 +668,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -711,15 +717,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -755,16 +760,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -795,15 +799,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
}
return defValue;
@@ -833,15 +836,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FRACTION) {
- return TypedValue.complexToFraction(
- data[index+AssetManager.STYLE_DATA], base, pbase);
+ return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -874,10 +876,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
- final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
+ final int resid = data[index + STYLE_RESOURCE_ID];
if (resid != 0) {
return resid;
}
@@ -902,10 +904,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
- return data[index + AssetManager.STYLE_DATA];
+ if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+ return data[index + STYLE_DATA];
}
return defValue;
}
@@ -939,7 +941,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -975,7 +977,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -1006,7 +1008,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return mResources.getTextArray(value.resourceId);
}
return null;
@@ -1027,7 +1029,7 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+ return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
}
/**
@@ -1043,8 +1045,8 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
- return mData[index + AssetManager.STYLE_TYPE];
+ index *= STYLE_NUM_ENTRIES;
+ return mData[index + STYLE_TYPE];
}
/**
@@ -1063,9 +1065,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL;
}
@@ -1084,11 +1086,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL
- || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+ || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
}
/**
@@ -1109,7 +1111,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return value;
}
return null;
@@ -1181,16 +1183,16 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+ final int index = i * STYLE_NUM_ENTRIES;
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
// Null the entry so that we can safely call getZzz().
- data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
+ data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
- final int attr = data[index + AssetManager.STYLE_DATA];
+ final int attr = data[index + STYLE_DATA];
if (attr == 0) {
// Useless data, ignore.
continue;
@@ -1231,45 +1233,44 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- final int type = data[index + AssetManager.STYLE_TYPE];
+ final int index = i * STYLE_NUM_ENTRIES;
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
continue;
}
changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
}
return changingConfig;
}
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
- outValue.data = data[index+AssetManager.STYLE_DATA];
- outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
- outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+ outValue.data = data[index + STYLE_DATA];
+ outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
+ outValue.resourceId = data[index + STYLE_RESOURCE_ID];
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
- outValue.density = data[index+AssetManager.STYLE_DENSITY];
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
+ outValue.density = data[index + STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
private CharSequence loadStringValueAt(int index) {
final int[] data = mData;
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
if (mXml != null) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]);
+ return mXml.getPooledString(data[index + STYLE_DATA]);
}
return null;
}
- return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
+ return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
}
/** @hide */
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index e6b95741..d4ccffb 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.annotation.Nullable;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -33,7 +34,7 @@
*
* {@hide}
*/
-final class XmlBlock {
+final class XmlBlock implements AutoCloseable {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
+ @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -478,13 +480,13 @@
* are doing! The given native object must exist for the entire lifetime
* of this newly creating XmlBlock.
*/
- XmlBlock(AssetManager assets, long xmlBlock) {
+ XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
mAssets = assets;
mNative = xmlBlock;
mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
}
- private final AssetManager mAssets;
+ private @Nullable final AssetManager mAssets;
private final long mNative;
/*package*/ final StringBlock mStrings;
private boolean mOpen = true;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b048977..1d22093 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,8 +110,8 @@
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
"android_util_EventLog.cpp",
- "android_util_MemoryIntArray.cpp",
"android_util_Log.cpp",
+ "android_util_MemoryIntArray.cpp",
"android_util_PathParser.cpp",
"android_util_Process.cpp",
"android_util_StringBlock.cpp",
@@ -191,6 +191,7 @@
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
"android_app_backup_FullBackup.cpp",
+ "android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
"android_animation_PropertyValuesHolder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f280c7a..5ae4a52 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,6 +123,7 @@
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
+extern int register_android_content_res_ApkAssets(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
@@ -1346,6 +1347,7 @@
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
+ REG_JNI(register_android_content_res_ApkAssets),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_MeasuredParagraph),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 48aef4a..ed032c7 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -28,7 +28,7 @@
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -205,7 +205,8 @@
NPE_CHECK_RETURN_ZERO(env, jpath);
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
+
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
if (NULL == mgr) {
builder->axes.clear();
return false;
@@ -217,27 +218,33 @@
return false;
}
- Asset* asset;
- if (isAsset) {
- asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- } else {
- asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
- Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ if (isAsset) {
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ } else if (cookie > 0) {
+ // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
+ asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
+ Asset::ACCESS_BUFFER);
+ } else {
+ asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ }
}
- if (NULL == asset) {
+ if (nullptr == asset) {
builder->axes.clear();
return false;
}
const void* buf = asset->getBuffer(false);
if (NULL == buf) {
- delete asset;
builder->axes.clear();
return false;
}
- sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset));
+ sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
+ asset.release()));
return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 09e37e1..49a24a3 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -361,7 +361,7 @@
code->sdkVersion = sdkVersion;
code->javaAssetManager = env->NewGlobalRef(jAssetMgr);
- code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
+ code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr);
if (obbDir != NULL) {
dirStr = env->GetStringUTFChars(obbDir, NULL);
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
new file mode 100644
index 0000000..7738d84
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "android-base/unique_fd.h"
+#include "androidfw/ApkAssets.h"
+#include "utils/misc.h"
+#include "utils/Trace.h"
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "nativehelper/ScopedUtfChars.h"
+
+using ::android::base::unique_fd;
+
+namespace android {
+
+static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
+ jboolean force_shared_lib, jboolean overlay) {
+ ScopedUtfChars path(env, java_path);
+ if (path.c_str() == nullptr) {
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
+
+ std::unique_ptr<const ApkAssets> apk_assets;
+ if (overlay) {
+ apk_assets = ApkAssets::LoadOverlay(path.c_str(), system);
+ } else if (force_shared_lib) {
+ apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
+ } else {
+ apk_assets = ApkAssets::Load(path.c_str(), system);
+ }
+
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
+ jstring friendly_name, jboolean system, jboolean force_shared_lib) {
+ ScopedUtfChars friendly_name_utf8(env, friendly_name);
+ if (friendly_name_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
+
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ unique_fd dup_fd(::dup(fd));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
+ friendly_name_utf8.c_str(),
+ system, force_shared_lib);
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
+ friendly_name_utf8.c_str(), dup_fd.get());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<ApkAssets*>(ptr);
+}
+
+static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return env->NewStringUTF(apk_assets->GetPath().c_str());
+}
+
+static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+}
+
+static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ (void)apk_assets;
+ return JNI_TRUE;
+}
+
+static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
+ ScopedUtfChars path_utf8(env, file_name);
+ if (path_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(),
+ Asset::AccessMode::ACCESS_RANDOM);
+ if (asset == nullptr) {
+ jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
+ return 0;
+ }
+
+ // DynamicRefTable is only needed when looking up resource references. Opening an XML file
+ // directly from an ApkAssets has no notion of proper resource references.
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
+// JNI registration.
+static const JNINativeMethod gApkAssetsMethods[] = {
+ {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad},
+ {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J",
+ (void*)NativeLoadFromFd},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
+ {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
+ {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
+ {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
+};
+
+int register_android_content_res_ApkAssets(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
+ arraysize(gApkAssetsMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 683b4c4..8be6ed8 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1,1851 +1,1449 @@
-/* //device/libs/android_runtime/android_util_AssetManager.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
#define LOG_TAG "asset"
-#include <android_runtime/android_util_AssetManager.h>
-
#include <inttypes.h>
#include <linux/capability.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include "androidfw/Asset.h"
-#include "androidfw/AssetManager.h"
-#include "androidfw/AttributeResolution.h"
-#include "androidfw/ResourceTypes.h"
+#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
+#include "android_runtime/android_util_AssetManager.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_util_Binder.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/MutexGuard.h"
+#include "androidfw/ResourceTypes.h"
#include "core_jni_helpers.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedStringChars.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include "nativehelper/JNIHelp.h"
+#include "nativehelper/ScopedPrimitiveArray.h"
+#include "nativehelper/ScopedStringChars.h"
+#include "nativehelper/ScopedUtfChars.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include "utils/String8.h"
+#include "utils/Trace.h"
extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
+using ::android::base::StringPrintf;
namespace android {
-static const bool kThrowOnBadId = false;
-
// ----------------------------------------------------------------------------
-static struct typedvalue_offsets_t
-{
- jfieldID mType;
- jfieldID mData;
- jfieldID mString;
- jfieldID mAssetCookie;
- jfieldID mResourceId;
- jfieldID mChangingConfigurations;
- jfieldID mDensity;
+static struct typedvalue_offsets_t {
+ jfieldID mType;
+ jfieldID mData;
+ jfieldID mString;
+ jfieldID mAssetCookie;
+ jfieldID mResourceId;
+ jfieldID mChangingConfigurations;
+ jfieldID mDensity;
} gTypedValueOffsets;
-static struct assetfiledescriptor_offsets_t
-{
- jfieldID mFd;
- jfieldID mStartOffset;
- jfieldID mLength;
+static struct assetfiledescriptor_offsets_t {
+ jfieldID mFd;
+ jfieldID mStartOffset;
+ jfieldID mLength;
} gAssetFileDescriptorOffsets;
-static struct assetmanager_offsets_t
-{
- jfieldID mObject;
+static struct assetmanager_offsets_t {
+ jfieldID mObject;
} gAssetManagerOffsets;
-static struct sparsearray_offsets_t
-{
- jclass classObject;
- jmethodID constructor;
- jmethodID put;
+static struct {
+ jfieldID native_ptr;
+} gApkAssetsFields;
+
+static struct sparsearray_offsets_t {
+ jclass classObject;
+ jmethodID constructor;
+ jmethodID put;
} gSparseArrayOffsets;
-static struct configuration_offsets_t
-{
- jclass classObject;
- jmethodID constructor;
- jfieldID mSmallestScreenWidthDpOffset;
- jfieldID mScreenWidthDpOffset;
- jfieldID mScreenHeightDpOffset;
+static struct configuration_offsets_t {
+ jclass classObject;
+ jmethodID constructor;
+ jfieldID mSmallestScreenWidthDpOffset;
+ jfieldID mScreenWidthDpOffset;
+ jfieldID mScreenHeightDpOffset;
} gConfigurationOffsets;
-jclass g_stringClass = NULL;
+jclass g_stringClass = nullptr;
// ----------------------------------------------------------------------------
-static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
- const Res_value& value, uint32_t ref, ssize_t block,
- uint32_t typeSpecFlags, ResTable_config* config = NULL);
+// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
+constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
+ return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
+}
-jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
- const Res_value& value, uint32_t ref, ssize_t block,
- uint32_t typeSpecFlags, ResTable_config* config)
-{
- env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
- env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
- static_cast<jint>(table->getTableCookie(block)));
- env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
- env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
- env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
- env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
- typeSpecFlags);
- if (config != NULL) {
- env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
- }
- return block;
+constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
+ return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
// This is called by zygote (running as user root) as part of preloadResources.
-static void verifySystemIdmaps()
-{
- pid_t pid;
- char system_id[10];
+static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
+ switch (pid_t pid = fork()) {
+ case -1:
+ PLOG(ERROR) << "failed to fork for idmap";
+ break;
- snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+ // child
+ case 0: {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
- switch (pid = fork()) {
- case -1:
- ALOGE("failed to fork for idmap: %s", strerror(errno));
- break;
- case 0: // child
- {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata;
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
+ if (capget(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capget";
+ exit(1);
+ }
- if (capget(&capheader, &capdata) != 0) {
- ALOGE("capget: %s\n", strerror(errno));
- exit(1);
- }
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capset";
+ exit(1);
+ }
- capdata.effective = capdata.permitted;
- if (capset(&capheader, &capdata) != 0) {
- ALOGE("capset: %s\n", strerror(errno));
- exit(1);
- }
+ if (setgid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setgid";
+ exit(1);
+ }
- if (setgid(AID_SYSTEM) != 0) {
- ALOGE("setgid: %s\n", strerror(errno));
- exit(1);
- }
+ if (setuid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setuid";
+ exit(1);
+ }
- if (setuid(AID_SYSTEM) != 0) {
- ALOGE("setuid: %s\n", strerror(errno));
- exit(1);
- }
+ // Generic idmap parameters
+ const char* argv[8];
+ int argc = 0;
+ struct stat st;
- // Generic idmap parameters
- const char* argv[8];
- int argc = 0;
- struct stat st;
+ memset(argv, 0, sizeof(argv));
+ argv[argc++] = AssetManager::IDMAP_BIN;
+ argv[argc++] = "--scan";
+ argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
+ argv[argc++] = AssetManager::TARGET_APK_PATH;
+ argv[argc++] = AssetManager::IDMAP_DIR;
- memset(argv, NULL, sizeof(argv));
- argv[argc++] = AssetManager::IDMAP_BIN;
- argv[argc++] = "--scan";
- argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
- argv[argc++] = AssetManager::TARGET_APK_PATH;
- argv[argc++] = AssetManager::IDMAP_DIR;
-
- // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
- char subdir[PROP_VALUE_MAX];
- int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
- if (len > 0) {
- String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
- if (stat(overlayPath.string(), &st) == 0) {
- argv[argc++] = overlayPath.string();
- }
- }
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
- }
-
- if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
- }
-
- // Finally, invoke idmap (if any overlay directory exists)
- if (argc > 5) {
- execv(AssetManager::IDMAP_BIN, (char* const*)argv);
- ALOGE("failed to execv for idmap: %s", strerror(errno));
- exit(1); // should never get here
- } else {
- exit(0);
- }
- }
- break;
- default: // parent
- waitpid(pid, NULL, 0);
- break;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-
-// this guy is exported to other jni routines
-AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
-{
- jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
- AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
- if (am != NULL) {
- return am;
- }
- jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
- return NULL;
-}
-
-static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
- jstring fileName, jint mode)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
- return -1;
- }
-
- if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
- && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return -1;
- }
-
- Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return -1;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return reinterpret_cast<jlong>(a);
-}
-
-static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
-{
- off64_t startOffset, length;
- int fd = a->openFileDescriptor(&startOffset, &length);
- delete a;
-
- if (fd < 0) {
- jniThrowException(env, "java/io/FileNotFoundException",
- "This file can not be opened as a file descriptor; it is probably compressed");
- return NULL;
- }
-
- jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
- if (offsets == NULL) {
- close(fd);
- return NULL;
- }
-
- offsets[0] = startOffset;
- offsets[1] = length;
-
- env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
-
- jobject fileDesc = jniCreateFileDescriptor(env, fd);
- if (fileDesc == NULL) {
- close(fd);
- return NULL;
- }
-
- return newParcelFileDescriptor(env, fileDesc);
-}
-
-static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
- jstring fileName, jlongArray outOffsets)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return returnParcelFileDescriptor(env, a, outOffsets);
-}
-
-static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName,
- jint mode)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return -1;
- }
-
- if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
- && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return -1;
- }
-
- Asset* a = cookie
- ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(),
- (Asset::AccessMode)mode)
- : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return -1;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return reinterpret_cast<jlong>(a);
-}
-
-static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName,
- jlongArray outOffsets)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- Asset* a = cookie
- ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM)
- : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return returnParcelFileDescriptor(env, a, outOffsets);
-}
-
-static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
- jstring fileName)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- AssetDir* dir = am->openDir(fileName8.c_str());
-
- if (dir == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- size_t N = dir->getFileCount();
-
- jobjectArray array = env->NewObjectArray(dir->getFileCount(),
- g_stringClass, NULL);
- if (array == NULL) {
- delete dir;
- return NULL;
- }
-
- for (size_t i=0; i<N; i++) {
- const String8& name = dir->getFileName(i);
- jstring str = env->NewStringUTF(name.string());
- if (str == NULL) {
- delete dir;
- return NULL;
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
+ "");
+ if (!overlay_theme_path.empty()) {
+ overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
+ if (stat(overlay_theme_path.c_str(), &st) == 0) {
+ argv[argc++] = overlay_theme_path.c_str();
}
- env->SetObjectArrayElement(array, i, str);
- env->DeleteLocalRef(str);
- }
+ }
- delete dir;
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::OVERLAY_DIR;
+ }
- return array;
+ if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
+ }
+
+ // Finally, invoke idmap (if any overlay directory exists)
+ if (argc > 5) {
+ execv(AssetManager::IDMAP_BIN, (char* const*)argv);
+ PLOG(ERROR) << "failed to execv for idmap";
+ exit(1); // should never get here
+ } else {
+ exit(0);
+ }
+ } break;
+
+ // parent
+ default:
+ waitpid(pid, nullptr, 0);
+ break;
+ }
}
-static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- //printf("Destroying Asset Stream: %p\n", a);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return;
- }
-
- delete a;
-}
-
-static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- uint8_t b;
- ssize_t res = a->read(&b, 1);
- return res == 1 ? b : -1;
-}
-
-static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle, jbyteArray bArray,
- jint off, jint len)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL || bArray == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- if (len == 0) {
- return 0;
- }
-
- jsize bLen = env->GetArrayLength(bArray);
- if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
- return -1;
- }
-
- jbyte* b = env->GetByteArrayElements(bArray, NULL);
- ssize_t res = a->read(b+off, len);
- env->ReleaseByteArrayElements(bArray, b, 0);
-
- if (res > 0) return static_cast<jint>(res);
-
- if (res < 0) {
- jniThrowException(env, "java/io/IOException", "");
- }
- return -1;
-}
-
-static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle,
- jlong offset, jint whence)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->seek(
- offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
-}
-
-static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->getLength();
-}
-
-static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->getRemainingLength();
-}
-
-static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
- jstring path, jboolean appAsLib)
-{
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
- jstring idmapPath)
-{
- ScopedUtfChars idmapPath8(env, idmapPath);
- if (idmapPath8.c_str() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
-
- return (res) ? (jint)cookie : 0;
-}
-
-static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
- jobject fileDescriptor, jstring debugPathName,
- jboolean appAsLib)
-{
- ScopedUtfChars debugPathName8(env, debugPathName);
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (fd < 0) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int dupfd = ::dup(fd);
- if (dupfd < 0) {
- jniThrowIOException(env, errno);
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_TRUE;
- }
- return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
-}
-
-static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
-{
- Vector<String8> locales;
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- am->getLocales(&locales, includeSystemLocales);
-
- const int N = locales.size();
-
- jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jstring str = env->NewStringUTF(locales[i].string());
- if (str == NULL) {
- return NULL;
- }
- env->SetObjectArrayElement(result, i, str);
- env->DeleteLocalRef(str);
- }
-
- return result;
-}
-
-static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, true /* include system locales */);
-}
-
-static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, false /* don't include system locales */);
-}
-
-static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
- jobject result = env->NewObject(gConfigurationOffsets.classObject,
- gConfigurationOffsets.constructor);
- if (result == NULL) {
- return NULL;
- }
-
- env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
- config.smallestScreenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
-
- return result;
-}
-
-static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
- const Vector<ResTable_config>& configs) {
- const int N = configs.size();
- jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jobject config = constructConfigurationObject(env, configs[i]);
- if (config == NULL) {
- env->DeleteLocalRef(result);
- return NULL;
- }
-
- env->SetObjectArrayElement(result, i, config);
- env->DeleteLocalRef(config);
- }
-
- return result;
-}
-
-static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- const ResTable& res(am->getResources());
- Vector<ResTable_config> configs;
- res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
-
- return getSizeConfigurationsInternal(env, configs);
-}
-
-static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
- jint mcc, jint mnc,
- jstring locale, jint orientation,
- jint touchscreen, jint density,
- jint keyboard, jint keyboardHidden,
- jint navigation,
- jint screenWidth, jint screenHeight,
- jint smallestScreenWidthDp,
- jint screenWidthDp, jint screenHeightDp,
- jint screenLayout, jint uiMode,
- jint colorMode, jint sdkVersion)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return;
- }
-
- ResTable_config config;
- memset(&config, 0, sizeof(config));
-
- const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
-
- // Constants duplicated from Java class android.content.res.Configuration.
- static const jint kScreenLayoutRoundMask = 0x300;
- static const jint kScreenLayoutRoundShift = 8;
-
- config.mcc = (uint16_t)mcc;
- config.mnc = (uint16_t)mnc;
- config.orientation = (uint8_t)orientation;
- config.touchscreen = (uint8_t)touchscreen;
- config.density = (uint16_t)density;
- config.keyboard = (uint8_t)keyboard;
- config.inputFlags = (uint8_t)keyboardHidden;
- config.navigation = (uint8_t)navigation;
- config.screenWidth = (uint16_t)screenWidth;
- config.screenHeight = (uint16_t)screenHeight;
- config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
- config.screenWidthDp = (uint16_t)screenWidthDp;
- config.screenHeightDp = (uint16_t)screenHeightDp;
- config.screenLayout = (uint8_t)screenLayout;
- config.uiMode = (uint8_t)uiMode;
- config.colorMode = (uint8_t)colorMode;
- config.sdkVersion = (uint16_t)sdkVersion;
- config.minorVersion = 0;
-
- // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
- // in C++. We must extract the round qualifier out of the Java screenLayout and put it
- // into screenLayout2.
- config.screenLayout2 =
- (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
-
- am->setConfiguration(config, locale8);
-
- if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
-}
-
-static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
- jstring name,
- jstring defType,
- jstring defPackage)
-{
- ScopedStringChars name16(env, name);
- if (name16.get() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
- : NULL;
- jsize defTypeLen = defType
- ? env->GetStringLength(defType) : 0;
- const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
- NULL))
- : NULL;
- jsize defPackageLen = defPackage
- ? env->GetStringLength(defPackage) : 0;
-
- jint ident = am->getResources().identifierForName(
- reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
- defType16, defTypeLen, defPackage16, defPackageLen);
-
- if (defPackage16) {
- env->ReleaseStringChars(defPackage,
- reinterpret_cast<const jchar*>(defPackage16));
- }
- if (defType16) {
- env->ReleaseStringChars(defType,
- reinterpret_cast<const jchar*>(defType16));
- }
-
- return ident;
-}
-
-static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- String16 str;
- if (name.package != NULL) {
- str.setTo(name.package, name.packageLen);
- }
- if (name.type8 != NULL || name.type != NULL) {
- if (str.size() > 0) {
- char16_t div = ':';
- str.append(&div, 1);
- }
- if (name.type8 != NULL) {
- str.append(String16(name.type8, name.typeLen));
- } else {
- str.append(name.type, name.typeLen);
- }
- }
- if (name.name8 != NULL || name.name != NULL) {
- if (str.size() > 0) {
- char16_t div = '/';
- str.append(&div, 1);
- }
- if (name.name8 != NULL) {
- str.append(String16(name.name8, name.nameLen));
- } else {
- str.append(name.name, name.nameLen);
- }
- }
-
- return env->NewString((const jchar*)str.string(), str.size());
-}
-
-static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.package != NULL) {
- return env->NewString((const jchar*)name.package, name.packageLen);
- }
-
- return NULL;
-}
-
-static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.type8 != NULL) {
- return env->NewStringUTF(name.type8);
- }
-
- if (name.type != NULL) {
- return env->NewString((const jchar*)name.type, name.typeLen);
- }
-
- return NULL;
-}
-
-static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.name8 != NULL) {
- return env->NewStringUTF(name.name8);
- }
-
- if (name.name != NULL) {
- return env->NewString((const jchar*)name.name, name.nameLen);
- }
-
- return NULL;
-}
-
-static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
- jint ident,
- jshort density,
- jobject outValue,
- jboolean resolve)
-{
- if (outValue == NULL) {
- jniThrowNullPointerException(env, "outValue");
- return 0;
- }
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- Res_value value;
- ResTable_config config;
- uint32_t typeSpecFlags;
- ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- uint32_t ref = ident;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- if (block >= 0) {
- return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
- }
-
- return static_cast<jint>(block);
-}
-
-static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
- jint ident, jint bagEntryId,
- jobject outValue, jboolean resolve)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- ssize_t block = -1;
- Res_value value;
-
- const ResTable::bag_entry* entry = NULL;
- uint32_t typeSpecFlags;
- ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
-
- for (ssize_t i=0; i<entryCount; i++) {
- if (((uint32_t)bagEntryId) == entry->map.name.ident) {
- block = entry->stringBlock;
- value = entry->map.value;
- }
- entry++;
- }
-
- res.unlock();
-
- if (block < 0) {
- return static_cast<jint>(block);
- }
-
- uint32_t ref = ident;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- if (block >= 0) {
- return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags);
- }
-
- return static_cast<jint>(block);
-}
-
-static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return am->getResources().getTableCount();
-}
-
-static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
- jint block)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
-}
-
-static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
- jint cookie)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- String8 name(am->getAssetPath(static_cast<int32_t>(cookie)));
- if (name.length() == 0) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
- return NULL;
- }
- jstring str = env->NewStringUTF(name.string());
- return str;
-}
-
-static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- const ResTable& res = am->getResources();
-
- jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject,
- gSparseArrayOffsets.constructor);
- const size_t N = res.getBasePackageCount();
- for (size_t i = 0; i < N; i++) {
- const String16 name = res.getBasePackageName(i);
- env->CallVoidMethod(
- sparseArray, gSparseArrayOffsets.put,
- static_cast<jint>(res.getBasePackageId(i)),
- env->NewString(reinterpret_cast<const jchar*>(name.string()),
- name.size()));
- }
- return sparseArray;
-}
-
-static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
-}
-
-static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- delete theme;
-}
-
-static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
- jlong themeHandle,
- jint styleRes,
- jboolean force)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- theme->applyStyle(styleRes, force ? true : false);
-}
-
-static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
- jlong destHandle, jlong srcHandle)
-{
- ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
- ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
- dest->setTo(*src);
-}
-
-static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- theme->clear();
-}
-
-static jint android_content_AssetManager_loadThemeAttributeValue(
- JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- const ResTable& res(theme->getResTable());
-
- Res_value value;
- // XXX value could be different in different configs!
- uint32_t typeSpecFlags = 0;
- ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
- uint32_t ref = 0;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
-}
-
-static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
- jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- return theme->getChangingConfigurations();
-}
-
-static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle, jint pri,
- jstring tag, jstring prefix)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- const ResTable& res(theme->getResTable());
- (void)res;
-
- // XXX Need to use params.
- theme->dumpToLog();
-}
-
-static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
- jlong themeToken,
- jint defStyleAttr,
- jint defStyleRes,
- jintArray inValues,
- jintArray attrs,
- jintArray outValues,
- jintArray outIndices)
-{
- if (themeToken == 0) {
- jniThrowNullPointerException(env, "theme token");
- return JNI_FALSE;
- }
- if (attrs == NULL) {
- jniThrowNullPointerException(env, "attrs");
- return JNI_FALSE;
- }
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- const jsize NI = env->GetArrayLength(attrs);
- const jsize NV = env->GetArrayLength(outValues);
- if (NV < (NI*STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
- return JNI_FALSE;
- }
-
- jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
- if (src == NULL) {
- return JNI_FALSE;
- }
-
- jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
- const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- if (baseDest == NULL) {
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return JNI_FALSE;
- }
-
- jint* indices = NULL;
- if (outIndices != NULL) {
- if (env->GetArrayLength(outIndices) > NI) {
- indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
- }
- }
-
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
- (uint32_t*) srcValues, NSV,
- (uint32_t*) src, NI,
- (uint32_t*) baseDest,
- (uint32_t*) indices);
-
- if (indices != NULL) {
- env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
- }
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
- env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken,
- jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length,
- jlong outValuesAddress, jlong outIndicesAddress) {
- jint* attrs = env->GetIntArrayElements(attrsObj, 0);
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
- uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
- uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
- ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
- reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
- env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
-}
-
-static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
- jlong xmlParserToken,
- jintArray attrs,
- jintArray outValues,
- jintArray outIndices)
-{
- if (xmlParserToken == 0) {
- jniThrowNullPointerException(env, "xmlParserToken");
- return JNI_FALSE;
- }
- if (attrs == NULL) {
- jniThrowNullPointerException(env, "attrs");
- return JNI_FALSE;
- }
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_FALSE;
- }
- const ResTable& res(am->getResources());
- ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
-
- const jsize NI = env->GetArrayLength(attrs);
- const jsize NV = env->GetArrayLength(outValues);
- if (NV < (NI*STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
- return JNI_FALSE;
- }
-
- jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
- if (src == NULL) {
- return JNI_FALSE;
- }
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- if (baseDest == NULL) {
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return JNI_FALSE;
- }
-
- jint* indices = NULL;
- if (outIndices != NULL) {
- if (env->GetArrayLength(outIndices) > NI) {
- indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
- }
- }
-
- bool result = RetrieveAttributes(&res, xmlParser,
- (uint32_t*) src, NI,
- (uint32_t*) baseDest,
- (uint32_t*) indices);
-
- if (indices != NULL) {
- env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
- }
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
- jint id)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- res.lock();
- const ResTable::bag_entry* defStyleEnt = NULL;
- ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
- res.unlock();
-
- return static_cast<jint>(bagOff);
-}
-
-static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
- jint id,
- jintArray outValues)
-{
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_FALSE;
- }
- const ResTable& res(am->getResources());
- ResTable_config config;
- Res_value value;
- ssize_t block;
-
- const jsize NV = env->GetArrayLength(outValues);
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- jint* dest = baseDest;
- if (dest == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
- return JNI_FALSE;
- }
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- const ResTable::bag_entry* arrayEnt = NULL;
- uint32_t arrayTypeSetFlags = 0;
- ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
- const ResTable::bag_entry* endArrayEnt = arrayEnt +
- (bagOff >= 0 ? bagOff : 0);
-
- int i = 0;
- uint32_t typeSetFlags;
- while (i < NV && arrayEnt < endArrayEnt) {
- block = arrayEnt->stringBlock;
- typeSetFlags = arrayTypeSetFlags;
- config.density = 0;
- value = arrayEnt->map.value;
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- //printf("Resolving attribute reference\n");
- ssize_t newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (kThrowOnBadId) {
- if (newBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return JNI_FALSE;
- }
- }
- if (newBlock >= 0) block = newBlock;
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- }
-
- //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
-
- // Write the final value back to Java.
- dest[STYLE_TYPE] = value.dataType;
- dest[STYLE_DATA] = value.data;
- dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
- dest[STYLE_RESOURCE_ID] = resid;
- dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- dest[STYLE_DENSITY] = config.density;
- dest += STYLE_NUM_ENTRIES;
- i+= STYLE_NUM_ENTRIES;
- arrayEnt++;
- }
-
- i /= STYLE_NUM_ENTRIES;
-
- res.unlock();
-
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
-
- return i;
-}
-
-static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return 0;
- }
-
- int32_t assetCookie = static_cast<int32_t>(cookie);
- Asset* a = assetCookie
- ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
- : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return 0;
- }
-
- const DynamicRefTable* dynamicRefTable =
- am->getResources().getDynamicRefTableForCookie(assetCookie);
- ResXMLTree* block = new ResXMLTree(dynamicRefTable);
- status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
- a->close();
- delete a;
-
- if (err != NO_ERROR) {
- jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
- return 0;
- }
-
- return reinterpret_cast<jlong>(block);
-}
-
-static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N * 2);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
- jint stringIndex = -1;
- jint stringBlock = 0;
- value = bag->map.value;
-
- // Take care of resolving the found resource to its final value.
- stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
- if (value.dataType == Res_value::TYPE_STRING) {
- stringIndex = value.data;
- }
-
- if (kThrowOnBadId) {
- if (stringBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
-
- //todo: It might be faster to allocate a C array to contain
- // the blocknums and indices, put them in there and then
- // do just one SetIntArrayRegion()
- env->SetIntArrayRegion(array, j, 1, &stringBlock);
- env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
- j = j + 2;
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
- if (env->ExceptionCheck()) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- size_t strLen = 0;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- value = bag->map.value;
- jstring str = NULL;
-
- // Take care of resolving the found resource to its final value.
- ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
- if (value.dataType == Res_value::TYPE_STRING) {
- const ResStringPool* pool = res.getTableStringBlock(block);
- const char* str8 = pool->string8At(value.data, &strLen);
- if (str8 != NULL) {
- str = env->NewStringUTF(str8);
- } else {
- const char16_t* str16 = pool->stringAt(value.data, &strLen);
- str = env->NewString(reinterpret_cast<const jchar*>(str16),
- strLen);
- }
-
- // If one of our NewString{UTF} calls failed due to memory, an
- // exception will be pending.
- if (env->ExceptionCheck()) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- env->SetObjectArrayElement(array, i, str);
-
- // str is not NULL at that point, otherwise ExceptionCheck would have been true.
- // If we have a large amount of strings in our array, we might
- // overflow the local reference table of the VM.
- env->DeleteLocalRef(str);
- }
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- value = bag->map.value;
-
- // Take care of resolving the found resource to its final value.
- ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
- if (value.dataType >= Res_value::TYPE_FIRST_INT
- && value.dataType <= Res_value::TYPE_LAST_INT) {
- int intVal = value.data;
- env->SetIntArrayRegion(array, i, 1, &intVal);
- }
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
- jint styleId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(styleId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- int resourceId = bag->map.name.ident;
- env->SetIntArrayRegion(array, i, 1, &resourceId);
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
-{
- if (isSystem) {
- verifySystemIdmaps();
- }
- AssetManager* am = new AssetManager();
- if (am == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
- return;
- }
-
- am->addDefaultAssets();
-
- ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
- env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
-}
-
-static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = (AssetManager*)
- (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
- ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
- if (am != NULL) {
- delete am;
- env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
- }
-}
-
-static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
-{
- return Asset::getGlobalCount();
-}
-
-static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
-{
- String8 alloc = Asset::getAssetAllocations();
- if (alloc.length() <= 0) {
- return NULL;
- }
-
- jstring str = env->NewStringUTF(alloc.string());
- return str;
-}
-
-static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
-{
- return AssetManager::getGlobalCount();
+static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
+ uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
+ ApkAssetsCookieToJavaCookie(cookie));
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
+ env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
+ if (config != nullptr) {
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
+ }
+ return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
}
// ----------------------------------------------------------------------------
-/*
- * JNI registration.
- */
-static const JNINativeMethod gAssetManagerMethods[] = {
- /* name, signature, funcPtr */
-
- // Basic asset stuff.
- { "openAsset", "(Ljava/lang/String;I)J",
- (void*) android_content_AssetManager_openAsset },
- { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*) android_content_AssetManager_openAssetFd },
- { "openNonAssetNative", "(ILjava/lang/String;I)J",
- (void*) android_content_AssetManager_openNonAssetNative },
- { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*) android_content_AssetManager_openNonAssetFdNative },
- { "list", "(Ljava/lang/String;)[Ljava/lang/String;",
- (void*) android_content_AssetManager_list },
- { "destroyAsset", "(J)V",
- (void*) android_content_AssetManager_destroyAsset },
- { "readAssetChar", "(J)I",
- (void*) android_content_AssetManager_readAssetChar },
- { "readAsset", "(J[BII)I",
- (void*) android_content_AssetManager_readAsset },
- { "seekAsset", "(JJI)J",
- (void*) android_content_AssetManager_seekAsset },
- { "getAssetLength", "(J)J",
- (void*) android_content_AssetManager_getAssetLength },
- { "getAssetRemainingLength", "(J)J",
- (void*) android_content_AssetManager_getAssetRemainingLength },
- { "addAssetPathNative", "(Ljava/lang/String;Z)I",
- (void*) android_content_AssetManager_addAssetPath },
- { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I",
- (void*) android_content_AssetManager_addAssetFd },
- { "addOverlayPathNative", "(Ljava/lang/String;)I",
- (void*) android_content_AssetManager_addOverlayPath },
- { "isUpToDate", "()Z",
- (void*) android_content_AssetManager_isUpToDate },
-
- // Resources.
- { "getLocales", "()[Ljava/lang/String;",
- (void*) android_content_AssetManager_getLocales },
- { "getNonSystemLocales", "()[Ljava/lang/String;",
- (void*) android_content_AssetManager_getNonSystemLocales },
- { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
- (void*) android_content_AssetManager_getSizeConfigurations },
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
- (void*) android_content_AssetManager_setConfiguration },
- { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*) android_content_AssetManager_getResourceIdentifier },
- { "getResourceName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceName },
- { "getResourcePackageName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourcePackageName },
- { "getResourceTypeName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceTypeName },
- { "getResourceEntryName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceEntryName },
- { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadResourceValue },
- { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadResourceBagValue },
- { "getStringBlockCount","()I",
- (void*) android_content_AssetManager_getStringBlockCount },
- { "getNativeStringBlock","(I)J",
- (void*) android_content_AssetManager_getNativeStringBlock },
- { "getCookieName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getCookieName },
- { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
- (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
-
- // Themes.
- { "newTheme", "()J",
- (void*) android_content_AssetManager_newTheme },
- { "deleteTheme", "(J)V",
- (void*) android_content_AssetManager_deleteTheme },
- { "applyThemeStyle", "(JIZ)V",
- (void*) android_content_AssetManager_applyThemeStyle },
- { "copyTheme", "(JJ)V",
- (void*) android_content_AssetManager_copyTheme },
- { "clearTheme", "(J)V",
- (void*) android_content_AssetManager_clearTheme },
- { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadThemeAttributeValue },
- { "getThemeChangingConfigurations", "(J)I",
- (void*) android_content_AssetManager_getThemeChangingConfigurations },
- { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
- (void*) android_content_AssetManager_dumpTheme },
- { "applyStyle","(JIIJ[IIJJ)V",
- (void*) android_content_AssetManager_applyStyle },
- { "resolveAttrs","(JII[I[I[I[I)Z",
- (void*) android_content_AssetManager_resolveAttrs },
- { "retrieveAttributes","(J[I[I[I)Z",
- (void*) android_content_AssetManager_retrieveAttributes },
- { "getArraySize","(I)I",
- (void*) android_content_AssetManager_getArraySize },
- { "retrieveArray","(I[I)I",
- (void*) android_content_AssetManager_retrieveArray },
-
- // XML files.
- { "openXmlAssetNative", "(ILjava/lang/String;)J",
- (void*) android_content_AssetManager_openXmlAssetNative },
-
- // Arrays.
- { "getArrayStringResource","(I)[Ljava/lang/String;",
- (void*) android_content_AssetManager_getArrayStringResource },
- { "getArrayStringInfo","(I)[I",
- (void*) android_content_AssetManager_getArrayStringInfo },
- { "getArrayIntResource","(I)[I",
- (void*) android_content_AssetManager_getArrayIntResource },
- { "getStyleAttributes","(I)[I",
- (void*) android_content_AssetManager_getStyleAttributes },
-
- // Bookkeeping.
- { "init", "(Z)V",
- (void*) android_content_AssetManager_init },
- { "destroy", "()V",
- (void*) android_content_AssetManager_destroy },
- { "getGlobalAssetCount", "()I",
- (void*) android_content_AssetManager_getGlobalAssetCount },
- { "getAssetAllocations", "()Ljava/lang/String;",
- (void*) android_content_AssetManager_getAssetAllocations },
- { "getGlobalAssetManagerCount", "()I",
- (void*) android_content_AssetManager_getGlobalAssetManagerCount },
+// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
+struct GuardedAssetManager : public ::AAssetManager {
+ Guarded<AssetManager2> guarded_assetmanager;
};
-int register_android_content_AssetManager(JNIEnv* env)
-{
- jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
- gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
- gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
- gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
- "Ljava/lang/CharSequence;");
- gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
- gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
- gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
- "changingConfigurations", "I");
- gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
+::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
+ jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
+ ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
+ if (am == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
+ return nullptr;
+ }
+ return am;
+}
- jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
- gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
- "Landroid/os/ParcelFileDescriptor;");
- gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
- gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
+ if (assetmanager == nullptr) {
+ return nullptr;
+ }
+ return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
+}
- jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
- gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
+ return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
+}
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
+ return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
+}
- jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
- gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
- gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
- "<init>", "()V");
- gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
- "(ILjava/lang/Object;)V");
+static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
+ jlongArray out_offsets) {
+ off64_t start_offset, length;
+ int fd = asset->openFileDescriptor(&start_offset, &length);
+ asset.reset();
- jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
- gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
- gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
- "<init>", "()V");
- gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "smallestScreenWidthDp", "I");
- gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenWidthDp", "I");
- gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenHeightDp", "I");
+ if (fd < 0) {
+ jniThrowException(env, "java/io/FileNotFoundException",
+ "This file can not be opened as a file descriptor; it is probably "
+ "compressed");
+ return nullptr;
+ }
- return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
- NELEM(gAssetManagerMethods));
+ jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
+ if (offsets == nullptr) {
+ close(fd);
+ return nullptr;
+ }
+
+ offsets[0] = start_offset;
+ offsets[1] = length;
+
+ env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
+
+ jobject file_desc = jniCreateFileDescriptor(env, fd);
+ if (file_desc == nullptr) {
+ close(fd);
+ return nullptr;
+ }
+ return newParcelFileDescriptor(env, file_desc);
+}
+
+static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ return Asset::getGlobalCount();
+}
+
+static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
+ String8 alloc = Asset::getAssetAllocations();
+ if (alloc.length() <= 0) {
+ return nullptr;
+ }
+ return env->NewStringUTF(alloc.string());
+}
+
+static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ // TODO(adamlesinski): Switch to AssetManager2.
+ return AssetManager::getGlobalCount();
+}
+
+static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
+ // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
+ // AssetManager2 in a contiguous block (GuardedAssetManager).
+ return reinterpret_cast<jlong>(new GuardedAssetManager());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<GuardedAssetManager*>(ptr);
+}
+
+static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jobjectArray apk_assets_array, jboolean invalidate_caches) {
+ ATRACE_NAME("AssetManager::SetApkAssets");
+
+ const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
+ std::vector<const ApkAssets*> apk_assets;
+ apk_assets.reserve(apk_assets_len);
+ for (jsize i = 0; i < apk_assets_len; i++) {
+ jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
+ if (obj == nullptr) {
+ std::string msg = StringPrintf("ApkAssets at index %d is null", i);
+ jniThrowNullPointerException(env, msg.c_str());
+ return;
+ }
+
+ jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+}
+
+static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
+ jstring locale, jint orientation, jint touchscreen, jint density,
+ jint keyboard, jint keyboard_hidden, jint navigation,
+ jint screen_width, jint screen_height,
+ jint smallest_screen_width_dp, jint screen_width_dp,
+ jint screen_height_dp, jint screen_layout, jint ui_mode,
+ jint color_mode, jint major_version) {
+ ATRACE_NAME("AssetManager::SetConfiguration");
+
+ ResTable_config configuration;
+ memset(&configuration, 0, sizeof(configuration));
+ configuration.mcc = static_cast<uint16_t>(mcc);
+ configuration.mnc = static_cast<uint16_t>(mnc);
+ configuration.orientation = static_cast<uint8_t>(orientation);
+ configuration.touchscreen = static_cast<uint8_t>(touchscreen);
+ configuration.density = static_cast<uint16_t>(density);
+ configuration.keyboard = static_cast<uint8_t>(keyboard);
+ configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
+ configuration.navigation = static_cast<uint8_t>(navigation);
+ configuration.screenWidth = static_cast<uint16_t>(screen_width);
+ configuration.screenHeight = static_cast<uint16_t>(screen_height);
+ configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
+ configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
+ configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
+ configuration.screenLayout = static_cast<uint8_t>(screen_layout);
+ configuration.uiMode = static_cast<uint8_t>(ui_mode);
+ configuration.colorMode = static_cast<uint8_t>(color_mode);
+ configuration.sdkVersion = static_cast<uint16_t>(major_version);
+
+ if (locale != nullptr) {
+ ScopedUtfChars locale_utf8(env, locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ configuration.setBcp47Locale(locale_utf8.c_str());
+ }
+
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
+ // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
+ // in C++. We must extract the round qualifier out of the Java screenLayout and put it
+ // into screenLayout2.
+ configuration.screenLayout2 =
+ static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetConfiguration(configuration);
+}
+
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+
+ jobject sparse_array =
+ env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
+
+ if (sparse_array == nullptr) {
+ // An exception is pending.
+ return nullptr;
+ }
+
+ assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
+ jstring jpackage_name = env->NewStringUTF(package_name.c_str());
+ if (jpackage_name == nullptr) {
+ // An exception is pending.
+ return;
+ }
+
+ env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
+ jpackage_name);
+ });
+ return sparse_array;
+}
+
+static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
+ ScopedUtfChars path_utf8(env, path);
+ if (path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<AssetDir> asset_dir =
+ assetmanager->OpenDir(path_utf8.c_str());
+ if (asset_dir == nullptr) {
+ jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
+ return nullptr;
+ }
+
+ const size_t file_count = asset_dir->getFileCount();
+
+ jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < file_count; i++) {
+ jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
+
+ // Check for errors creating the strings (if malformed or no memory).
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, i, java_string);
+
+ // If we have a large amount of string in our array, we might overflow the
+ // local reference table of the VM.
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
+static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
+ jint access_mode) {
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str());
+
+ if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
+ access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return 0;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset =
+ assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
+ jlongArray out_offsets) {
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str());
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return nullptr;
+ }
+ return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
+}
+
+static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path, jint access_mode) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str());
+
+ if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
+ access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return 0;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
+ static_cast<Asset::AccessMode>(access_mode));
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
+ static_cast<Asset::AccessMode>(access_mode));
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path, jlongArray out_offsets) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str());
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return nullptr;
+ }
+ return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
+}
+
+static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str());
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+
+ // May be nullptr.
+ const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
+
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
+static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jshort density, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ ApkAssetsCookie cookie =
+ assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
+ static_cast<uint16_t>(density), &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t ref = static_cast<uint32_t>(resid);
+ if (resolve_references) {
+ cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ }
+ return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
+}
+
+static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jint bag_entry_id, jobject typed_value) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t type_spec_flags = bag->type_spec_flags;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ const Res_value* bag_value = nullptr;
+ for (const ResolvedBag::Entry& entry : bag) {
+ if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
+ cookie = entry.cookie;
+ bag_value = &entry.value;
+
+ // Keep searching (the old implementation did that).
+ }
+ }
+
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ Res_value value = *bag_value;
+ uint32_t ref = static_cast<uint32_t>(resid);
+ ResTable_config selected_config;
+ cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
+}
+
+static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ for (uint32_t i = 0; i < bag->entry_count; i++) {
+ jint attr_resid = bag->entries[i].key;
+ env->SetIntArrayRegion(array, i, 1, &attr_resid);
+ }
+ return array;
+}
+
+static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ for (uint32_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+
+ // Resolve any references to their final value.
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return nullptr;
+ }
+
+ if (value.dataType == Res_value::TYPE_STRING) {
+ const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
+ const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
+
+ jstring java_string = nullptr;
+ size_t str_len;
+ const char* str_utf8 = pool->string8At(value.data, &str_len);
+ if (str_utf8 != nullptr) {
+ java_string = env->NewStringUTF(str_utf8);
+ } else {
+ const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
+ java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
+ }
+
+ // Check for errors creating the strings (if malformed or no memory).
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, i, java_string);
+
+ // If we have a large amount of string in our array, we might overflow the
+ // local reference table of the VM.
+ env->DeleteLocalRef(java_string);
+ }
+ }
+ return array;
+}
+
+static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count * 2);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ return nullptr;
+ }
+
+ jint string_index = -1;
+ if (value.dataType == Res_value::TYPE_STRING) {
+ string_index = static_cast<jint>(value.data);
+ }
+
+ buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
+ buffer[(i * 2) + 1] = string_index;
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ return nullptr;
+ }
+
+ if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
+ buffer[i] = static_cast<jint>(value.data);
+ }
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+ return static_cast<jint>(bag->entry_count);
+}
+
+static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jintArray out_data) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+
+ const jsize out_data_length = env->GetArrayLength(out_data);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
+ return -1;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
+ if (buffer == nullptr) {
+ return -1;
+ }
+
+ jint* cursor = buffer;
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ selected_config.density = 0;
+ uint32_t flags = bag->type_spec_flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
+ return -1;
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ }
+
+ cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
+ cursor[STYLE_DATA] = static_cast<jint>(value.data);
+ cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
+ cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
+ cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
+ cursor += STYLE_NUM_ENTRIES;
+ }
+ env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
+ return static_cast<jint>(bag->entry_count);
+}
+
+static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
+ jstring def_type, jstring def_package) {
+ ScopedUtfChars name_utf8(env, name);
+ if (name_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ std::string type;
+ if (def_type != nullptr) {
+ ScopedUtfChars type_utf8(env, def_type);
+ CHECK(type_utf8.c_str() != nullptr);
+ type = type_utf8.c_str();
+ }
+
+ std::string package;
+ if (def_package != nullptr) {
+ ScopedUtfChars package_utf8(env, def_package);
+ CHECK(package_utf8.c_str() != nullptr);
+ package = package_utf8.c_str();
+ }
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
+}
+
+static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ std::string result;
+ if (name.package != nullptr) {
+ result.append(name.package, name.package_len);
+ }
+
+ if (name.type != nullptr || name.type16 != nullptr) {
+ if (!result.empty()) {
+ result += ":";
+ }
+
+ if (name.type != nullptr) {
+ result.append(name.type, name.type_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+ }
+ }
+
+ if (name.entry != nullptr || name.entry16 != nullptr) {
+ if (!result.empty()) {
+ result += "/";
+ }
+
+ if (name.entry != nullptr) {
+ result.append(name.entry, name.entry_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+ }
+ }
+ return env->NewStringUTF(result.c_str());
+}
+
+static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.package != nullptr) {
+ return env->NewStringUTF(name.package);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.type != nullptr) {
+ return env->NewStringUTF(name.type);
+ } else if (name.type16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.entry != nullptr) {
+ return env->NewStringUTF(name.entry);
+ } else if (name.entry16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
+ }
+ return nullptr;
+}
+
+static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
+ jboolean exclude_system) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<std::string> locales =
+ assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
+
+ jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ size_t idx = 0;
+ for (const std::string& locale : locales) {
+ jstring java_string = env->NewStringUTF(locale.c_str());
+ if (java_string == nullptr) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(array, idx++, java_string);
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
+static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
+ jobject result =
+ env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
+ if (result == nullptr) {
+ return nullptr;
+ }
+
+ env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
+ config.smallestScreenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
+ return result;
+}
+
+static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<ResTable_config> configurations =
+ assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
+
+ jobjectArray array =
+ env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ size_t idx = 0;
+ for (const ResTable_config& configuration : configurations) {
+ jobject java_configuration = ConstructConfigurationObject(env, configuration);
+ if (java_configuration == nullptr) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, idx++, java_configuration);
+ env->DeleteLocalRef(java_configuration);
+ }
+ return array;
+}
+
+static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
+ jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+ uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
+ uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
+
+ jsize attrs_len = env->GetArrayLength(java_attrs);
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return;
+ }
+
+ ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+ static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ out_values, out_indices);
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+}
+
+static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint def_style_attr, jint def_style_resid, jintArray java_values,
+ jintArray java_attrs, jintArray out_java_values,
+ jintArray out_java_indices) {
+ const jsize attrs_len = env->GetArrayLength(java_attrs);
+ const jsize out_values_len = env->GetArrayLength(out_java_values);
+ if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
+ return JNI_FALSE;
+ }
+
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return JNI_FALSE;
+ }
+
+ jint* values = nullptr;
+ jsize values_len = 0;
+ if (java_values != nullptr) {
+ values_len = env->GetArrayLength(java_values);
+ values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
+ if (values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
+ if (out_values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ return JNI_FALSE;
+ }
+
+ jint* out_indices = nullptr;
+ if (out_java_indices != nullptr) {
+ jsize out_indices_len = env->GetArrayLength(out_java_indices);
+ if (out_indices_len > attrs_len) {
+ out_indices =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
+ if (out_indices == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ bool result = ResolveAttrs(
+ theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
+ reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
+ attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
+ if (out_indices != nullptr) {
+ env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
+ }
+
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jlong xml_parser_ptr, jintArray java_attrs,
+ jintArray out_java_values, jintArray out_java_indices) {
+ const jsize attrs_len = env->GetArrayLength(java_attrs);
+ const jsize out_values_len = env->GetArrayLength(out_java_values);
+ if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
+ return JNI_FALSE;
+ }
+
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return JNI_FALSE;
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
+ if (out_values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return JNI_FALSE;
+ }
+
+ jint* out_indices = nullptr;
+ if (out_java_indices != nullptr) {
+ jsize out_indices_len = env->GetArrayLength(out_java_indices);
+ if (out_indices_len > attrs_len) {
+ out_indices =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
+ if (out_indices == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+
+ bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
+ reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ reinterpret_cast<uint32_t*>(out_values),
+ reinterpret_cast<uint32_t*>(out_indices));
+
+ if (out_indices != nullptr) {
+ env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
+ }
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
+}
+
+static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ delete reinterpret_cast<Theme*>(theme_ptr);
+}
+
+static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint resid, jboolean force) {
+ // AssetManager is accessed via the theme, so grab an explicit lock here.
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+
+ // TODO(adamlesinski): Consider surfacing exception when result is failure.
+ // CTS currently expects no exceptions from this method.
+ // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
+ // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
+}
+
+static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
+ jlong src_theme_ptr) {
+ Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
+ Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
+ if (!dst_theme->SetTo(*src_theme)) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Themes are from different AssetManagers");
+ }
+}
+
+static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ reinterpret_cast<Theme*>(theme_ptr)->Clear();
+}
+
+static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint resid, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t ref = 0u;
+ if (resolve_references) {
+ ResTable_config selected_config;
+ cookie =
+ theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ }
+ return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
+}
+
+static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint priority, jstring tag, jstring prefix) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ (void) theme;
+ (void) priority;
+ (void) tag;
+ (void) prefix;
+}
+
+static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
+ jlong theme_ptr) {
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ return static_cast<jint>(theme->GetChangingConfigurations());
+}
+
+static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ delete reinterpret_cast<Asset*>(asset_ptr);
+}
+
+static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ uint8_t b;
+ ssize_t res = asset->read(&b, sizeof(b));
+ return res == sizeof(b) ? static_cast<jint>(b) : -1;
+}
+
+static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
+ jint offset, jint len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ jsize buffer_len = env->GetArrayLength(java_buffer);
+ if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
+ offset > buffer_len - len) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
+ return -1;
+ }
+
+ ScopedByteArrayRW byte_array(env, java_buffer);
+ if (byte_array.get() == nullptr) {
+ return -1;
+ }
+
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ ssize_t res = asset->read(byte_array.get() + offset, len);
+ if (res < 0) {
+ jniThrowException(env, "java/io/IOException", "");
+ return -1;
+ }
+ return res > 0 ? static_cast<jint>(res) : -1;
+}
+
+static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
+ jint whence) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->seek(
+ static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
+}
+
+static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getLength());
+}
+
+static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getRemainingLength());
+}
+
+// ----------------------------------------------------------------------------
+
+// JNI registration.
+static const JNINativeMethod gAssetManagerMethods[] = {
+ // AssetManager setup methods.
+ {"nativeCreate", "()J", (void*)NativeCreate},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
+ (void*)NativeSetConfiguration},
+ {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
+ (void*)NativeGetAssignedPackageIdentifiers},
+
+ // AssetManager file methods.
+ {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
+ {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
+ {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenAssetFd},
+ {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
+ {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenNonAssetFd},
+ {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+
+ // AssetManager resource methods.
+ {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
+ {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
+ (void*)NativeGetResourceBagValue},
+ {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
+ {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
+ (void*)NativeGetResourceStringArray},
+ {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
+ {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
+ {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
+ {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+
+ // AssetManager resource name/ID methods.
+ {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)NativeGetResourceIdentifier},
+ {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
+ {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
+ {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
+ {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+ {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
+ {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeConfigurations},
+
+ // Style attribute related methods.
+ {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
+ {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
+ {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
+
+ // Theme related methods.
+ {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
+ {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
+ {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
+ {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
+ {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
+ {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
+ (void*)NativeThemeGetAttributeValue},
+ {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
+ {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
+
+ // AssetInputStream methods.
+ {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
+ {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
+ {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
+ {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
+ {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
+ {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
+
+ // System/idmap related methods.
+ {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
+
+ // Global management/debug methods.
+ {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
+ {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
+ {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
+};
+
+int register_android_content_AssetManager(JNIEnv* env) {
+ jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
+ gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
+
+ jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
+ gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
+ gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
+ gTypedValueOffsets.mString =
+ GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
+ gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
+ gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
+ gTypedValueOffsets.mChangingConfigurations =
+ GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
+ gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
+
+ jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
+ gAssetFileDescriptorOffsets.mFd =
+ GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
+ gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
+ gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+
+ jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
+ gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+
+ jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
+ gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
+ gSparseArrayOffsets.constructor =
+ GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
+ gSparseArrayOffsets.put =
+ GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
+
+ jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
+ gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
+ gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
+ gConfigurationOffsets.mSmallestScreenWidthDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
+ gConfigurationOffsets.mScreenWidthDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
+ gConfigurationOffsets.mScreenHeightDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
+
+ return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
+ NELEM(gAssetManagerMethods));
}
}; // namespace android
diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h
index 8dd9337..2c1e357 100644
--- a/core/jni/include/android_runtime/android_util_AssetManager.h
+++ b/core/jni/include/android_runtime/android_util_AssetManager.h
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-#ifndef android_util_AssetManager_H
-#define android_util_AssetManager_H
+#ifndef ANDROID_RUNTIME_ASSETMANAGER_H
+#define ANDROID_RUNTIME_ASSETMANAGER_H
-#include <androidfw/AssetManager.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/MutexGuard.h"
#include "jni.h"
namespace android {
-extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr);
+extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
+extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
+extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager);
-}
+} // namespace android
-#endif
+#endif // ANDROID_RUNTIME_ASSETMANAGER_H
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 251b2e7..70d5216 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -145,6 +145,7 @@
"tests/TypeWrappers_test.cpp",
"tests/ZipUtils_test.cpp",
],
+ static_libs: ["libgmock"],
target: {
android: {
srcs: [
@@ -171,6 +172,7 @@
// Actual benchmarks.
"tests/AssetManager2_bench.cpp",
+ "tests/AttributeResolution_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index da0205d..8f58f74 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG ATRACE_TAG_RESOURCES
-
#include "androidfw/ApkAssets.h"
#include <algorithm>
@@ -27,7 +25,6 @@
#include "android-base/utf8.h"
#include "utils/Compat.h"
#include "utils/FileMap.h"
-#include "utils/Trace.h"
#include "ziparchive/zip_archive.h"
#include "androidfw/Asset.h"
@@ -105,8 +102,6 @@
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
- ATRACE_CALL();
-
::ZipArchiveHandle unmanaged_handle;
int32_t result;
if (fd >= 0) {
@@ -163,7 +158,6 @@
}
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
- ATRACE_CALL();
CHECK(zip_handle_ != nullptr);
::ZipString name(path.c_str());
@@ -231,12 +225,16 @@
while ((result = ::Next(cookie, &entry, &name)) == 0) {
StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- dirs.insert(
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
- } else if (!leaf_file_path.empty()) {
- f(leaf_file_path, kFileTypeRegular);
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
}
}
::EndIteration(cookie);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 415d3e3..d9f1293 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -36,6 +36,31 @@
namespace android {
+struct FindEntryResult {
+ // A pointer to the resource table entry for this resource.
+ // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
+ // a ResTable_map_entry and processed as a bag/map.
+ const ResTable_entry* entry;
+
+ // The configuration for which the resulting entry was defined. This is already swapped to host
+ // endianness.
+ ResTable_config config;
+
+ // The bitmask of configuration axis with which the resource value varies.
+ uint32_t type_flags;
+
+ // The dynamic package ID map for the package from which this resource came from.
+ const DynamicRefTable* dynamic_ref_table;
+
+ // The string pool reference to the type's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef type_string_ref;
+
+ // The string pool reference to the entry's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef entry_string_ref;
+};
+
AssetManager2::AssetManager2() {
memset(&configuration_, 0, sizeof(configuration_));
}
@@ -44,6 +69,7 @@
bool invalidate_caches) {
apk_assets_ = apk_assets;
BuildDynamicRefTable();
+ RebuildFilterList();
if (invalidate_caches) {
InvalidateCaches(static_cast<uint32_t>(-1));
}
@@ -74,12 +100,14 @@
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
- package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
+ DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
+ ref_table.mAssignedPackageId = package_id;
+ ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
PackageGroup* package_group = &package_groups_[idx];
// Add the package and to the set of packages with the same ID.
- package_group->packages_.push_back(package.get());
+ package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
// Add the package name -> build time ID mappings.
@@ -94,7 +122,7 @@
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
const auto package_groups_end = package_groups_.end();
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
- const std::string& package_name = iter->packages_[0]->GetPackageName();
+ const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table.mAssignedPackageId);
@@ -105,20 +133,33 @@
void AssetManager2::DumpToLog() const {
base::ScopedLogSeverity _log(base::INFO);
+ LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
+
std::string list;
+ for (const auto& apk_assets : apk_assets_) {
+ base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str());
+ }
+ LOG(INFO) << "ApkAssets: " << list;
+
+ list = "";
for (size_t i = 0; i < package_ids_.size(); i++) {
if (package_ids_[i] != 0xff) {
- base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
+ base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]);
}
}
LOG(INFO) << "Package ID map: " << list;
for (const auto& package_group: package_groups_) {
- list = "";
- for (const auto& package : package_group.packages_) {
- base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
- }
- LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
+ list = "";
+ for (const auto& package : package_group.packages_) {
+ const LoadedPackage* loaded_package = package.loaded_package_;
+ base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(),
+ loaded_package->GetPackageId(),
+ (loaded_package->IsDynamic() ? " dynamic" : ""));
+ }
+ LOG(INFO) << base::StringPrintf("PG (%02x): ",
+ package_group.dynamic_ref_table.mAssignedPackageId)
+ << list;
}
}
@@ -157,53 +198,55 @@
configuration_ = configuration;
if (diff) {
+ RebuildFilterList();
InvalidateCaches(static_cast<uint32_t>(diff));
}
}
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) {
- ATRACE_CALL();
+ bool exclude_mipmap) const {
+ ATRACE_NAME("AssetManager::GetResourceConfigurations");
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
- for (const LoadedPackage* package : package_group.packages_) {
- if (exclude_system && package->IsSystem()) {
+ for (const ConfiguredPackage& package : package_group.packages_) {
+ if (exclude_system && package.loaded_package_->IsSystem()) {
continue;
}
- package->CollectConfigurations(exclude_mipmap, &configurations);
+ package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
}
}
return configurations;
}
std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
- bool merge_equivalent_languages) {
- ATRACE_CALL();
+ bool merge_equivalent_languages) const {
+ ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
for (const PackageGroup& package_group : package_groups_) {
- for (const LoadedPackage* package : package_group.packages_) {
- if (exclude_system && package->IsSystem()) {
+ for (const ConfiguredPackage& package : package_group.packages_) {
+ if (exclude_system && package.loaded_package_->IsSystem()) {
continue;
}
- package->CollectLocales(merge_equivalent_languages, &locales);
+ package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
}
}
return locales;
}
-std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
+ Asset::AccessMode mode) const {
const std::string new_path = "assets/" + filename;
return OpenNonAsset(new_path, mode);
}
std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode) {
+ Asset::AccessMode mode) const {
const std::string new_path = "assets/" + filename;
return OpenNonAsset(new_path, cookie, mode);
}
-std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
- ATRACE_CALL();
+std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
+ ATRACE_NAME("AssetManager::OpenDir");
std::string full_path = "assets/" + dirname;
std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
@@ -236,8 +279,7 @@
// is inconsistent for split APKs.
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
Asset::AccessMode mode,
- ApkAssetsCookie* out_cookie) {
- ATRACE_CALL();
+ ApkAssetsCookie* out_cookie) const {
for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
if (asset) {
@@ -255,8 +297,8 @@
}
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
- ApkAssetsCookie cookie, Asset::AccessMode mode) {
- ATRACE_CALL();
+ ApkAssetsCookie cookie,
+ Asset::AccessMode mode) const {
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
}
@@ -264,14 +306,13 @@
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
- bool stop_at_first_match, FindEntryResult* out_entry) {
- ATRACE_CALL();
-
+ bool /*stop_at_first_match*/,
+ FindEntryResult* out_entry) const {
// Might use this if density_override != 0.
ResTable_config density_override_config;
// Select our configuration or generate a density override configuration.
- ResTable_config* desired_config = &configuration_;
+ const ResTable_config* desired_config = &configuration_;
if (density_override != 0 && density_override != configuration_.density) {
density_override_config = configuration_;
density_override_config.density = density_override;
@@ -285,55 +326,135 @@
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
- const uint16_t entry_id = get_entry_id(resid);
+ const uint16_t entry_idx = get_entry_id(resid);
- const uint8_t idx = package_ids_[package_id];
- if (idx == 0xff) {
+ const uint8_t package_idx = package_ids_[package_id];
+ if (package_idx == 0xff) {
LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
return kInvalidCookie;
}
- FindEntryResult best_entry;
- ApkAssetsCookie best_cookie = kInvalidCookie;
- uint32_t cumulated_flags = 0u;
-
- const PackageGroup& package_group = package_groups_[idx];
+ const PackageGroup& package_group = package_groups_[package_idx];
const size_t package_count = package_group.packages_.size();
- FindEntryResult current_entry;
- for (size_t i = 0; i < package_count; i++) {
- const LoadedPackage* loaded_package = package_group.packages_[i];
- if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) {
+
+ ApkAssetsCookie best_cookie = kInvalidCookie;
+ const LoadedPackage* best_package = nullptr;
+ const ResTable_type* best_type = nullptr;
+ const ResTable_config* best_config = nullptr;
+ ResTable_config best_config_copy;
+ uint32_t best_offset = 0u;
+ uint32_t type_flags = 0u;
+
+ // If desired_config is the same as the set configuration, then we can use our filtered list
+ // and we don't need to match the configurations, since they already matched.
+ const bool use_fast_path = desired_config == &configuration_;
+
+ for (size_t pi = 0; pi < package_count; pi++) {
+ const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
+ const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
+ ApkAssetsCookie cookie = package_group.cookies_[pi];
+
+ // If the type IDs are offset in this package, we need to take that into account when searching
+ // for a type.
+ const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
+ if (UNLIKELY(type_spec == nullptr)) {
continue;
}
- cumulated_flags |= current_entry.type_flags;
+ uint16_t local_entry_idx = entry_idx;
- const ResTable_config* current_config = current_entry.config;
- const ResTable_config* best_config = best_entry.config;
- if (best_cookie == kInvalidCookie ||
- current_config->isBetterThan(*best_config, desired_config) ||
- (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) {
- best_entry = current_entry;
- best_cookie = package_group.cookies_[i];
- if (stop_at_first_match) {
- break;
+ // If there is an IDMAP supplied with this package, translate the entry ID.
+ if (type_spec->idmap_entries != nullptr) {
+ if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
+ // There is no mapping, so the resource is not meant to be in this overlay package.
+ continue;
+ }
+ }
+
+ type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
+
+ // If the package is an overlay, then even configurations that are the same MUST be chosen.
+ const bool package_is_overlay = loaded_package->IsOverlay();
+
+ const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
+ if (use_fast_path) {
+ const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
+ const size_t type_count = candidate_configs.size();
+ for (uint32_t i = 0; i < type_count; i++) {
+ const ResTable_config& this_config = candidate_configs[i];
+
+ // We can skip calling ResTable_config::match() because we know that all candidate
+ // configurations that do NOT match have been filtered-out.
+ if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
+ (package_is_overlay && this_config.compare(*best_config) == 0)) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ const ResTable_type* type_chunk = filtered_group.types[i];
+ const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
+ if (offset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+
+ best_cookie = cookie;
+ best_package = loaded_package;
+ best_type = type_chunk;
+ best_config = &this_config;
+ best_offset = offset;
+ }
+ }
+ } else {
+ // This is the slower path, which doesn't use the filtered list of configurations.
+ // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
+ // and fill in any new fields that did not exist when the APK was compiled.
+ // Furthermore when selecting configurations we can't just record the pointer to the
+ // ResTable_config, we must copy it.
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ ResTable_config this_config;
+ this_config.copyFromDtoH((*iter)->config);
+
+ if (this_config.match(*desired_config)) {
+ if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
+ (package_is_overlay && this_config.compare(*best_config) == 0)) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+ if (offset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+
+ best_cookie = cookie;
+ best_package = loaded_package;
+ best_type = *iter;
+ best_config_copy = this_config;
+ best_config = &best_config_copy;
+ best_offset = offset;
+ }
+ }
}
}
}
- if (best_cookie == kInvalidCookie) {
+ if (UNLIKELY(best_cookie == kInvalidCookie)) {
return kInvalidCookie;
}
- *out_entry = best_entry;
+ const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+ if (UNLIKELY(best_entry == nullptr)) {
+ return kInvalidCookie;
+ }
+
+ out_entry->entry = best_entry;
+ out_entry->config = *best_config;
+ out_entry->type_flags = type_flags;
+ out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
+ out_entry->entry_string_ref =
+ StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
- out_entry->type_flags = cumulated_flags;
return best_cookie;
}
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
- ATRACE_CALL();
-
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
FindEntryResult entry;
ApkAssetsCookie cookie =
FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
@@ -341,7 +462,8 @@
return false;
}
- const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
+ const LoadedPackage* package =
+ apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package == nullptr) {
return false;
}
@@ -369,7 +491,7 @@
return true;
}
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
FindEntryResult entry;
ApkAssetsCookie cookie =
FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
@@ -383,9 +505,7 @@
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
uint16_t density_override, Res_value* out_value,
ResTable_config* out_selected_config,
- uint32_t* out_flags) {
- ATRACE_CALL();
-
+ uint32_t* out_flags) const {
FindEntryResult entry;
ApkAssetsCookie cookie =
FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
@@ -402,7 +522,7 @@
// Create a reference since we can't represent this complex type as a Res_value.
out_value->dataType = Res_value::TYPE_REFERENCE;
out_value->data = resid;
- *out_selected_config = *entry.config;
+ *out_selected_config = entry.config;
*out_flags = entry.type_flags;
return cookie;
}
@@ -414,7 +534,7 @@
// Convert the package ID to the runtime assigned package ID.
entry.dynamic_ref_table->lookupResourceValue(out_value);
- *out_selected_config = *entry.config;
+ *out_selected_config = entry.config;
*out_flags = entry.type_flags;
return cookie;
}
@@ -422,16 +542,13 @@
ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config,
uint32_t* in_out_flags,
- uint32_t* out_last_reference) {
- ATRACE_CALL();
+ uint32_t* out_last_reference) const {
constexpr const int kMaxIterations = 20;
for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
in_out_value->data != 0u && iteration < kMaxIterations;
iteration++) {
- if (out_last_reference != nullptr) {
- *out_last_reference = in_out_value->data;
- }
+ *out_last_reference = in_out_value->data;
uint32_t new_flags = 0u;
cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
in_out_value, in_out_selected_config, &new_flags);
@@ -450,7 +567,7 @@
}
const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
- ATRACE_CALL();
+ ATRACE_NAME("AssetManager::GetBag");
auto cached_iter = cached_bags_.find(resid);
if (cached_iter != cached_bags_.end()) {
@@ -492,7 +609,8 @@
// Attributes, arrays, etc don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
+ resid);
return nullptr;
}
}
@@ -524,7 +642,8 @@
const ResolvedBag* parent_bag = GetBag(parent_resid);
if (parent_bag == nullptr) {
// Failed to get the parent that should exist.
- LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
+ LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
+ resid);
return nullptr;
}
@@ -543,7 +662,8 @@
uint32_t child_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(child_key)) {
if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
+ resid);
return nullptr;
}
}
@@ -582,7 +702,8 @@
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
+ resid);
return nullptr;
}
}
@@ -638,7 +759,7 @@
uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
const std::string& fallback_type,
- const std::string& fallback_package) {
+ const std::string& fallback_package) const {
StringPiece package_name, type, entry;
if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
return 0u;
@@ -670,7 +791,8 @@
const static std::u16string kAttrPrivate16 = u"^attr-private";
for (const PackageGroup& package_group : package_groups_) {
- for (const LoadedPackage* package : package_group.packages_) {
+ for (const ConfiguredPackage& package_impl : package_group.packages_) {
+ const LoadedPackage* package = package_impl.loaded_package_;
if (package_name != package->GetPackageName()) {
// All packages in the same group are expected to have the same package name.
break;
@@ -692,6 +814,32 @@
return 0u;
}
+void AssetManager2::RebuildFilterList() {
+ for (PackageGroup& group : package_groups_) {
+ for (ConfiguredPackage& impl : group.packages_) {
+ // Destroy it.
+ impl.filtered_configs_.~ByteBucketArray();
+
+ // Re-create it.
+ new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
+
+ // Create the filters here.
+ impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) {
+ FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index);
+ const auto iter_end = spec->types + spec->type_count;
+ for (auto iter = spec->types; iter != iter_end; ++iter) {
+ ResTable_config this_config;
+ this_config.copyFromDtoH((*iter)->config);
+ if (this_config.match(configuration_)) {
+ group.configurations.push_back(this_config);
+ group.types.push_back(*iter);
+ }
+ }
+ });
+ }
+ }
+}
+
void AssetManager2::InvalidateCaches(uint32_t diff) {
if (diff == 0xffffffffu) {
// Everything must go.
@@ -743,7 +891,7 @@
};
bool Theme::ApplyStyle(uint32_t resid, bool force) {
- ATRACE_CALL();
+ ATRACE_NAME("Theme::ApplyStyle");
const ResolvedBag* bag = asset_manager_->GetBag(resid);
if (bag == nullptr) {
@@ -872,7 +1020,7 @@
ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config,
uint32_t* in_out_type_spec_flags,
- uint32_t* out_last_ref) {
+ uint32_t* out_last_ref) const {
if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
uint32_t new_flags;
cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 60e3845..f912af4 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -20,13 +20,18 @@
#include <log/log.h>
+#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeFinder.h"
-#include "androidfw/ResourceTypes.h"
constexpr bool kDebugStyles = false;
namespace android {
+// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
+static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
+ return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
+}
+
class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
@@ -44,58 +49,53 @@
};
class BagAttributeFinder
- : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
public:
- BagAttributeFinder(const ResTable::bag_entry* start,
- const ResTable::bag_entry* end)
- : BackTrackingAttributeFinder(start, end) {}
+ BagAttributeFinder(const ResolvedBag* bag)
+ : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
+ bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
+ }
- inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
- return entry->map.name.ident;
+ inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const {
+ return entry->key;
}
};
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
- uint32_t def_style_res, uint32_t* src_values,
- size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values,
- uint32_t* out_indices) {
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+ uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
def_style_attr, def_style_res);
}
- const ResTable& res = theme->getResTable();
+ AssetManager2* assetmanager = theme->GetAssetManager();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_bag_type_set_flags = 0;
+ uint32_t def_style_flags = 0u;
if (def_style_attr != 0) {
Res_value value;
- if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
+ if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = value.data;
}
}
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
// Retrieve the default style bag, if requested.
- const ResTable::bag_entry* def_style_start = nullptr;
- uint32_t def_style_type_set_flags = 0;
- ssize_t bag_off = def_style_res != 0
- ? res.getBagLocked(def_style_res, &def_style_start,
- &def_style_type_set_flags)
- : -1;
- def_style_type_set_flags |= def_style_bag_type_set_flags;
- const ResTable::bag_entry* const def_style_end =
- def_style_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
+ const ResolvedBag* default_style_bag = nullptr;
+ if (def_style_res != 0) {
+ default_style_bag = assetmanager->GetBag(def_style_res);
+ if (default_style_bag != nullptr) {
+ def_style_flags |= default_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -106,7 +106,7 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ssize_t block = -1;
+ ApkAssetsCookie cookie = kInvalidCookie;
uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
@@ -122,15 +122,14 @@
value.dataType = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else {
- const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
- if (def_style_entry != def_style_end) {
- block = def_style_entry->stringBlock;
- type_set_flags = def_style_type_set_flags;
- value = def_style_entry->map.value;
+ const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
+ if (entry != def_style_attr_finder.end()) {
+ cookie = entry->cookie;
+ type_set_flags = def_style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -140,22 +139,26 @@
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block =
- theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ ApkAssetsCookie new_cookie =
+ theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
if (kDebugStyles) {
ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
- if (new_block >= 0) {
+ // If we still don't have a value for this attribute, try to find it in the theme!
+ ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+ if (new_cookie != kInvalidCookie) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ new_cookie =
+ assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
if (kDebugStyles) {
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -169,7 +172,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = -1;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -179,9 +182,7 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -195,90 +196,80 @@
out_values += STYLE_NUM_ENTRIES;
}
- res.unlock();
-
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
return true;
}
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
- theme, def_style_attr, def_style_res, xml_parser);
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+ def_style_attr, def_style_resid, xml_parser);
}
- const ResTable& res = theme->getResTable();
+ AssetManager2* assetmanager = theme->GetAssetManager();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_bag_type_set_flags = 0;
+ uint32_t def_style_flags = 0u;
if (def_style_attr != 0) {
Res_value value;
- if (theme->getAttribute(def_style_attr, &value,
- &def_style_bag_type_set_flags) >= 0) {
+ if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_res = value.data;
+ def_style_resid = value.data;
}
}
}
- // Retrieve the style class associated with the current XML tag.
- int style = 0;
- uint32_t style_bag_type_set_flags = 0;
+ // Retrieve the style resource ID associated with the current XML tag's style attribute.
+ uint32_t style_resid = 0u;
+ uint32_t style_flags = 0u;
if (xml_parser != nullptr) {
ssize_t idx = xml_parser->indexOfStyle();
if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
if (value.dataType == value.TYPE_ATTRIBUTE) {
- if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
+ // Resolve the attribute with out theme.
+ if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
value.dataType = Res_value::TYPE_NULL;
}
}
+
if (value.dataType == value.TYPE_REFERENCE) {
- style = value.data;
+ style_resid = value.data;
}
}
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
// Retrieve the default style bag, if requested.
- const ResTable::bag_entry* def_style_attr_start = nullptr;
- uint32_t def_style_type_set_flags = 0;
- ssize_t bag_off = def_style_res != 0
- ? res.getBagLocked(def_style_res, &def_style_attr_start,
- &def_style_type_set_flags)
- : -1;
- def_style_type_set_flags |= def_style_bag_type_set_flags;
- const ResTable::bag_entry* const def_style_attr_end =
- def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder def_style_attr_finder(def_style_attr_start,
- def_style_attr_end);
+ const ResolvedBag* default_style_bag = nullptr;
+ if (def_style_resid != 0) {
+ default_style_bag = assetmanager->GetBag(def_style_resid);
+ if (default_style_bag != nullptr) {
+ def_style_flags |= default_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag);
// Retrieve the style class bag, if requested.
- const ResTable::bag_entry* style_attr_start = nullptr;
- uint32_t style_type_set_flags = 0;
- bag_off =
- style != 0
- ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
- : -1;
- style_type_set_flags |= style_bag_type_set_flags;
- const ResTable::bag_entry* const style_attr_end =
- style_attr_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
+ const ResolvedBag* xml_style_bag = nullptr;
+ if (style_resid != 0) {
+ xml_style_bag = assetmanager->GetBag(style_resid);
+ if (xml_style_bag != nullptr) {
+ style_flags |= xml_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder xml_style_attr_finder(xml_style_bag);
// Retrieve the XML attributes, if requested.
- static const ssize_t kXmlBlock = 0x10000000;
XmlAttributeFinder xml_attr_finder(xml_parser);
- const size_t xml_attr_end =
- xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -289,8 +280,8 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ssize_t block = kXmlBlock;
- uint32_t type_set_flags = 0;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ uint32_t type_set_flags = 0u;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -302,7 +293,7 @@
// Walk through the xml attributes looking for the requested attribute.
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
- if (xml_attr_idx != xml_attr_end) {
+ if (xml_attr_idx != xml_attr_finder.end()) {
// We found the attribute we were looking for.
xml_parser->getAttributeValue(xml_attr_idx, &value);
if (kDebugStyles) {
@@ -312,12 +303,12 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the style class values looking for the requested attribute.
- const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
- if (style_attr_entry != style_attr_end) {
+ const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
+ if (entry != xml_style_attr_finder.end()) {
// We found the attribute we were looking for.
- block = style_attr_entry->stringBlock;
- type_set_flags = style_type_set_flags;
- value = style_attr_entry->map.value;
+ cookie = entry->cookie;
+ type_set_flags = style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -326,25 +317,25 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the default style values looking for the requested attribute.
- const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
- if (def_style_attr_entry != def_style_attr_end) {
+ const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
+ if (entry != def_style_attr_finder.end()) {
// We found the attribute we were looking for.
- block = def_style_attr_entry->stringBlock;
- type_set_flags = style_type_set_flags;
- value = def_style_attr_entry->map.value;
+ cookie = entry->cookie;
+ type_set_flags = def_style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
- uint32_t resid = 0;
+ uint32_t resid = 0u;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block =
- theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
- if (new_block >= 0) {
- block = new_block;
+ ApkAssetsCookie new_cookie =
+ theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
}
if (kDebugStyles) {
@@ -352,14 +343,15 @@
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
- if (new_block >= 0) {
+ ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+ if (new_cookie != kInvalidCookie) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
- if (new_block >= 0) {
- block = new_block;
+ new_cookie =
+ assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
}
if (kDebugStyles) {
@@ -375,7 +367,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -385,9 +377,7 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -402,36 +392,28 @@
out_values += STYLE_NUM_ENTRIES;
}
- res.unlock();
-
// out_indices must NOT be nullptr.
out_indices[0] = indices_idx;
}
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
- uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices) {
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
ResTable_config config;
Res_value value;
int indices_idx = 0;
- // Now lock down the resource object and start pulling stuff from it.
- res->lock();
-
// Retrieve the XML attributes, if requested.
const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
- static const ssize_t kXmlBlock = 0x10000000;
-
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
- ssize_t block = kXmlBlock;
- uint32_t type_set_flags = 0;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ uint32_t type_set_flags = 0u;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -450,28 +432,27 @@
cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
- uint32_t resid = 0;
+ uint32_t resid = 0u;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- // printf("Resolving attribute reference\n");
- ssize_t new_block = res->resolveReference(&value, block, &resid,
- &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ ApkAssetsCookie new_cookie =
+ assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ cookie = kInvalidCookie;
}
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -485,8 +466,6 @@
out_values += STYLE_NUM_ENTRIES;
}
- res->unlock();
-
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 28548e2..04d506a 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -44,44 +44,6 @@
constexpr const static int kAppPackageId = 0x7f;
-// Element of a TypeSpec array. See TypeSpec.
-struct Type {
- // The configuration for which this type defines entries.
- // This is already converted to host endianness.
- ResTable_config configuration;
-
- // Pointer to the mmapped data where entry definitions are kept.
- const ResTable_type* type;
-};
-
-// TypeSpec is going to be immediately proceeded by
-// an array of Type structs, all in the same block of memory.
-struct TypeSpec {
- // Pointer to the mmapped data where flags are kept.
- // Flags denote whether the resource entry is public
- // and under which configurations it varies.
- const ResTable_typeSpec* type_spec;
-
- // Pointer to the mmapped data where the IDMAP mappings for this type
- // exist. May be nullptr if no IDMAP exists.
- const IdmapEntry_header* idmap_entries;
-
- // The number of types that follow this struct.
- // There is a type for each configuration
- // that entries are defined for.
- size_t type_count;
-
- // Trick to easily access a variable number of Type structs
- // proceeding this struct, and to ensure their alignment.
- const Type types[0];
-};
-
-// TypeSpecPtr points to the block of memory that holds
-// a TypeSpec struct, followed by an array of Type structs.
-// TypeSpecPtr is a managed pointer that knows how to delete
-// itself.
-using TypeSpecPtr = util::unique_cptr<TypeSpec>;
-
namespace {
// Builder that helps accumulate Type structs and then create a single
@@ -95,21 +57,22 @@
}
void AddType(const ResTable_type* type) {
- ResTable_config config;
- config.copyFromDtoH(type->config);
- types_.push_back(Type{config, type});
+ types_.push_back(type);
}
TypeSpecPtr Build() {
// Check for overflow.
- if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+ using ElementType = const ResTable_type*;
+ if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
+ types_.size()) {
return {};
}
- TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
+ TypeSpec* type_spec =
+ (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
type_spec->type_spec = header_;
type_spec->idmap_entries = idmap_header_;
type_spec->type_count = types_.size();
- memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
+ memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
return TypeSpecPtr(type_spec);
}
@@ -118,7 +81,7 @@
const ResTable_typeSpec* header_;
const IdmapEntry_header* idmap_header_;
- std::vector<Type> types_;
+ std::vector<const ResTable_type*> types_;
};
} // namespace
@@ -162,18 +125,17 @@
return true;
}
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset,
- size_t entry_idx) {
+static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
// Check that the offset is aligned.
if (entry_offset & 0x03) {
- LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned.";
+ LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
return false;
}
// Check that the offset doesn't overflow.
if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
// Overflow in offset.
- LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large.";
+ LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
return false;
}
@@ -181,7 +143,7 @@
entry_offset += dtohl(type->entriesStart);
if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
- LOG(ERROR) << "Entry offset at index " << entry_idx
+ LOG(ERROR) << "Entry at offset " << entry_offset
<< " is too large. No room for ResTable_entry.";
return false;
}
@@ -191,13 +153,13 @@
const size_t entry_size = dtohs(entry->size);
if (entry_size < sizeof(*entry)) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too small.";
return false;
}
if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too large.";
return false;
}
@@ -205,7 +167,7 @@
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
- LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx
+ LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
<< " for type " << (int)type->id << ".";
return false;
}
@@ -214,12 +176,12 @@
reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
const size_t value_size = dtohs(value->size);
if (value_size < sizeof(Res_value)) {
- LOG(ERROR) << "Res_value at index " << entry_idx << " is too small.";
+ LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
return false;
}
if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
- LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx
+ LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
<< " is too large.";
return false;
}
@@ -228,119 +190,76 @@
const size_t map_entry_count = dtohl(map->count);
size_t map_entries_start = entry_offset + entry_size;
if (map_entries_start & 0x03) {
- LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset.";
+ LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
return false;
}
// Each entry is sizeof(ResTable_map) big.
if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
- LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << ".";
+ LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
return false;
}
}
return true;
}
-bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx,
- const ResTable_config& config, FindEntryResult* out_entry) const {
- const ResTable_config* best_config = nullptr;
- const ResTable_type* best_type = nullptr;
- uint32_t best_offset = 0;
+const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
+ uint16_t entry_index) {
+ uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
+ if (entry_offset == ResTable_type::NO_ENTRY) {
+ return nullptr;
+ }
+ return GetEntryFromOffset(type_chunk, entry_offset);
+}
- for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) {
- const Type* type = &type_spec_ptr->types[i];
- const ResTable_type* type_chunk = type->type;
+uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ const size_t entry_count = dtohl(type_chunk->entryCount);
+ const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
- if (type->configuration.match(config) &&
- (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
- // The configuration matches and is better than the previous selection.
- // Find the entry value if it exists for this configuration.
- const size_t entry_count = dtohl(type_chunk->entryCount);
- const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+ // Check if there is the desired entry in this type.
- // Check if there is the desired entry in this type.
-
- if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
- // This is encoded as a sparse map, so perform a binary search.
- const ResTable_sparseTypeEntry* sparse_indices =
- reinterpret_cast<const ResTable_sparseTypeEntry*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
- const ResTable_sparseTypeEntry* result =
- std::lower_bound(sparse_indices, sparse_indices_end, entry_idx,
- [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
- return dtohs(entry.idx) < entry_idx;
- });
-
- if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) {
- // No entry found.
- continue;
- }
-
- // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
- // the real offset divided by 4.
- best_offset = uint32_t{dtohs(result->offset)} * 4u;
- } else {
- if (entry_idx >= entry_count) {
- // This entry cannot be here.
- continue;
- }
-
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+ if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
+ // This is encoded as a sparse map, so perform a binary search.
+ const ResTable_sparseTypeEntry* sparse_indices =
+ reinterpret_cast<const ResTable_sparseTypeEntry*>(
reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const uint32_t offset = dtohl(entry_offsets[entry_idx]);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
+ const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
+ const ResTable_sparseTypeEntry* result =
+ std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
+ [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
+ return dtohs(entry.idx) < entry_idx;
+ });
- // There is an entry for this resource, record it.
- best_offset = offset;
- }
-
- best_config = &type->configuration;
- best_type = type_chunk;
+ if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+ // No entry found.
+ return ResTable_type::NO_ENTRY;
}
+
+ // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
+ // the real offset divided by 4.
+ return uint32_t{dtohs(result->offset)} * 4u;
}
- if (best_type == nullptr) {
- return false;
+ // This type is encoded as a dense array.
+ if (entry_index >= entry_count) {
+ // This entry cannot be here.
+ return ResTable_type::NO_ENTRY;
}
- if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) {
- return false;
- }
-
- const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart));
-
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1);
- out_entry->type_flags = dtohl(flags[entry_idx]);
- out_entry->entry = best_entry;
- out_entry->config = best_config;
- out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
- out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
- return true;
+ const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
+ return dtohl(entry_offsets[entry_index]);
}
-bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
- FindEntryResult* out_entry) const {
- ATRACE_CALL();
-
- // If the type IDs are offset in this package, we need to take that into account when searching
- // for a type.
- const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
- if (UNLIKELY(ptr == nullptr)) {
- return false;
+const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
+ uint32_t offset) {
+ if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
+ return nullptr;
}
-
- // If there is an IDMAP supplied with this package, translate the entry ID.
- if (ptr->idmap_entries != nullptr) {
- if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) {
- // There is no mapping, so the resource is not meant to be in this overlay package.
- return false;
- }
- }
- return FindEntry(ptr, entry_idx, config, out_entry);
+ return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
+ offset + dtohl(type_chunk->entriesStart));
}
void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -348,7 +267,7 @@
const static std::u16string kMipMap = u"mipmap";
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
- const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+ const TypeSpecPtr& type_spec = type_specs_[i];
if (type_spec != nullptr) {
if (exclude_mipmap) {
const int type_idx = type_spec->type_spec->id - 1;
@@ -369,8 +288,11 @@
}
}
- for (size_t j = 0; j < type_spec->type_count; j++) {
- out_configs->insert(type_spec->types[j].configuration);
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ ResTable_config config;
+ config.copyFromDtoH((*iter)->config);
+ out_configs->insert(config);
}
}
}
@@ -380,10 +302,12 @@
char temp_locale[RESTABLE_MAX_LOCALE_LEN];
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
- const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+ const TypeSpecPtr& type_spec = type_specs_[i];
if (type_spec != nullptr) {
- for (size_t j = 0; j < type_spec->type_count; j++) {
- const ResTable_config& configuration = type_spec->types[j].configuration;
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ ResTable_config configuration;
+ configuration.copyFromDtoH((*iter)->config);
if (configuration.locale != 0) {
configuration.getBcp47Locale(temp_locale, canonicalize);
std::string locale(temp_locale);
@@ -411,17 +335,17 @@
return 0u;
}
- for (size_t ti = 0; ti < type_spec->type_count; ti++) {
- const Type* type = &type_spec->types[ti];
- size_t entry_count = dtohl(type->type->entryCount);
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ const ResTable_type* type = *iter;
+ size_t entry_count = dtohl(type->entryCount);
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
+ reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
const uint32_t offset = dtohl(entry_offsets[entry_idx]);
if (offset != ResTable_type::NO_ENTRY) {
- const ResTable_entry* entry =
- reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) +
- dtohl(type->type->entriesStart) + offset);
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
@@ -433,8 +357,7 @@
return 0u;
}
-const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
- const uint8_t package_id = get_package_id(resid);
+const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
for (const auto& loaded_package : packages_) {
if (loaded_package->GetPackageId() == package_id) {
return loaded_package.get();
@@ -446,7 +369,7 @@
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
const LoadedIdmap* loaded_idmap,
bool system, bool load_as_shared_library) {
- ATRACE_CALL();
+ ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
// typeIdOffset was added at some point, but we still must recognize apps built before this
@@ -486,14 +409,10 @@
util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
&loaded_package->package_name_);
- // A TypeSpec builder. We use this to accumulate the set of Types
- // available for a TypeSpec, and later build a single, contiguous block
- // of memory that holds all the Types together with the TypeSpec.
- std::unique_ptr<TypeSpecPtrBuilder> types_builder;
-
- // Keep track of the last seen type index. Since type IDs are 1-based,
- // this records their index, which is 0-based (type ID - 1).
- uint8_t last_type_idx = 0;
+ // A map of TypeSpec builders, each associated with an type index.
+ // We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
+ // contiguous block of memory that holds all the Types together with the TypeSpec.
+ std::unordered_map<int, std::unique_ptr<TypeSpecPtrBuilder>> type_builder_map;
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
while (iter.HasNext()) {
@@ -525,30 +444,6 @@
} break;
case RES_TABLE_TYPE_SPEC_TYPE: {
- ATRACE_NAME("LoadTableTypeSpec");
-
- // Starting a new TypeSpec, so finish the old one if there was one.
- if (types_builder) {
- TypeSpecPtr type_spec_ptr = types_builder->Build();
- if (type_spec_ptr == nullptr) {
- LOG(ERROR) << "Too many type configurations, overflow detected.";
- return {};
- }
-
- // We only add the type to the package if there is no IDMAP, or if the type is
- // overlaying something.
- if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
- // If this is an overlay, insert it at the target type ID.
- if (type_spec_ptr->idmap_entries != nullptr) {
- last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
- }
- loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
- }
-
- types_builder = {};
- last_type_idx = 0;
- }
-
const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
if (type_spec == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
@@ -583,8 +478,6 @@
return {};
}
- last_type_idx = type_spec->id - 1;
-
// If this is an overlay, associate the mapping of this type to the target type
// from the IDMAP.
const IdmapEntry_header* idmap_entry_header = nullptr;
@@ -592,7 +485,13 @@
idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
}
- types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+ std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
+ if (builder_ptr == nullptr) {
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+ } else {
+ LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
+ type_spec->id);
+ }
} break;
case RES_TABLE_TYPE_TYPE: {
@@ -607,12 +506,15 @@
}
// Type chunks must be preceded by their TypeSpec chunks.
- if (!types_builder || type->id - 1 != last_type_idx) {
- LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE.";
+ std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
+ if (builder_ptr != nullptr) {
+ builder_ptr->AddType(type);
+ } else {
+ LOG(ERROR) << StringPrintf(
+ "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
+ type->id);
return {};
}
-
- types_builder->AddType(type);
} break;
case RES_TABLE_LIBRARY_TYPE: {
@@ -638,7 +540,7 @@
arraysize(entry_iter->packageName), &package_name);
if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) {
- LOG(ERROR) << base::StringPrintf(
+ LOG(ERROR) << StringPrintf(
"Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
dtohl(entry_iter->packageId), package_name.c_str());
return {};
@@ -651,14 +553,20 @@
} break;
default:
- LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
}
}
- // Finish the last TypeSpec.
- if (types_builder) {
- TypeSpecPtr type_spec_ptr = types_builder->Build();
+ if (iter.HadError()) {
+ LOG(ERROR) << iter.GetLastError();
+ return {};
+ }
+
+ // Flatten and construct the TypeSpecs.
+ for (auto& entry : type_builder_map) {
+ uint8_t type_idx = static_cast<uint8_t>(entry.first);
+ TypeSpecPtr type_spec_ptr = entry.second->Build();
if (type_spec_ptr == nullptr) {
LOG(ERROR) << "Too many type configurations, overflow detected.";
return {};
@@ -669,43 +577,17 @@
if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
// If this is an overlay, insert it at the target type ID.
if (type_spec_ptr->idmap_entries != nullptr) {
- last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
+ type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
}
- loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+ loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
}
}
- if (iter.HadError()) {
- LOG(ERROR) << iter.GetLastError();
- return {};
- }
return std::move(loaded_package);
}
-bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
- FindEntryResult* out_entry) const {
- ATRACE_CALL();
-
- const uint8_t package_id = get_package_id(resid);
- const uint8_t type_id = get_type_id(resid);
- const uint16_t entry_id = get_entry_id(resid);
-
- if (UNLIKELY(type_id == 0)) {
- LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
- return false;
- }
-
- for (const auto& loaded_package : packages_) {
- if (loaded_package->GetPackageId() == package_id) {
- return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry);
- }
- }
- return false;
-}
-
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
bool load_as_shared_library) {
- ATRACE_CALL();
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -752,7 +634,7 @@
} break;
default:
- LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
}
}
@@ -767,7 +649,7 @@
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap, bool system,
bool load_as_shared_library) {
- ATRACE_CALL();
+ ATRACE_NAME("LoadedArsc::LoadTable");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
@@ -784,7 +666,7 @@
break;
default:
- LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
}
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index b033137..ef08897 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -69,6 +69,8 @@
Entry entries[0];
};
+struct FindEntryResult;
+
// AssetManager2 is the main entry point for accessing assets and resources.
// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
class AssetManager2 {
@@ -127,7 +129,7 @@
// If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
// will be excluded from the list.
std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
- bool exclude_mipmap = false);
+ bool exclude_mipmap = false) const;
// Returns all the locales for which there are resources defined. This includes resource
// locales in all the ApkAssets set for this AssetManager.
@@ -136,24 +138,24 @@
// If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
// and de-duped in the resulting list.
std::set<std::string> GetResourceLocales(bool exclude_system = false,
- bool merge_equivalent_languages = false);
+ bool merge_equivalent_languages = false) const;
// Searches the set of APKs loaded by this AssetManager and opens the first one found located
// in the assets/ directory.
// `mode` controls how the file is opened.
//
// NOTE: The loaded APKs are searched in reverse order.
- std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
+ std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
// Opens a file within the assets/ directory of the APK specified by `cookie`.
// `mode` controls how the file is opened.
std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode);
+ Asset::AccessMode mode) const;
// Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
// of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
// The entries are sorted by their ASCII name.
- std::unique_ptr<AssetDir> OpenDir(const std::string& dirname);
+ std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
// Searches the set of APKs loaded by this AssetManager and opens the first one found.
// `mode` controls how the file is opened.
@@ -161,24 +163,24 @@
//
// NOTE: The loaded APKs are searched in reverse order.
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
- ApkAssetsCookie* out_cookie = nullptr);
+ ApkAssetsCookie* out_cookie = nullptr) const;
// Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
// This is typically used to open a specific AndroidManifest.xml, or a binary XML file
// referenced by a resource lookup with GetResource().
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode);
+ Asset::AccessMode mode) const;
// Populates the `out_name` parameter with resource name information.
// Utf8 strings are preferred, and only if they are unavailable are
// the Utf16 variants populated.
// Returns false if the resource was not found or the name was missing/corrupt.
- bool GetResourceName(uint32_t resid, ResourceName* out_name);
+ bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
// Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
// See ResTable_config for the list of configuration axis.
// Returns false if the resource was not found.
- bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
+ bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
// Finds the resource ID assigned to `resource_name`.
// `resource_name` must be of the form '[package:][type/]entry'.
@@ -186,7 +188,7 @@
// If no type is specified in `resource_name`, then `fallback_type` is used as the type.
// Returns 0x0 if no resource by that name was found.
uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
- const std::string& fallback_package = {});
+ const std::string& fallback_package = {}) const;
// Retrieves the best matching resource with ID `resid`. The resource value is filled into
// `out_value` and the configuration for the selected value is populated in `out_selected_config`.
@@ -199,7 +201,7 @@
// this function logs if the resource was a map/bag type before returning kInvalidCookie.
ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
Res_value* out_value, ResTable_config* out_selected_config,
- uint32_t* out_flags);
+ uint32_t* out_flags) const;
// Resolves the resource reference in `in_out_value` if the data type is
// Res_value::TYPE_REFERENCE.
@@ -215,7 +217,7 @@
// it was not found.
ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
- uint32_t* out_last_reference);
+ uint32_t* out_last_reference) const;
// Retrieves the best matching bag/map resource with ID `resid`.
// This method will resolve all parent references for this bag and merge keys with the child.
@@ -233,9 +235,9 @@
std::unique_ptr<Theme> NewTheme();
template <typename Func>
- void ForEachPackage(Func func) {
+ void ForEachPackage(Func func) const {
for (const PackageGroup& package_group : package_groups_) {
- func(package_group.packages_.front()->GetPackageName(),
+ func(package_group.packages_.front().loaded_package_->GetPackageName(),
package_group.dynamic_ref_table.mAssignedPackageId);
}
}
@@ -260,7 +262,7 @@
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- FindEntryResult* out_entry);
+ FindEntryResult* out_entry) const;
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
@@ -270,13 +272,43 @@
// bitmask `diff`.
void InvalidateCaches(uint32_t diff);
+ // Triggers the re-construction of lists of types that match the set configuration.
+ // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
+ void RebuildFilterList();
+
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
+ // A collection of configurations and their associated ResTable_type that match the current
+ // AssetManager configuration.
+ struct FilteredConfigGroup {
+ std::vector<ResTable_config> configurations;
+ std::vector<const ResTable_type*> types;
+ };
+
+ // Represents an single package.
+ struct ConfiguredPackage {
+ // A pointer to the immutable, loaded package info.
+ const LoadedPackage* loaded_package_;
+
+ // A mutable AssetManager-specific list of configurations that match the AssetManager's
+ // current configuration. This is used as an optimization to avoid checking every single
+ // candidate configuration when looking up resources.
+ ByteBucketArray<FilteredConfigGroup> filtered_configs_;
+ };
+
+ // Represents a logical package, which can be made up of many individual packages. Each package
+ // in a PackageGroup shares the same package name and package ID.
struct PackageGroup {
- std::vector<const LoadedPackage*> packages_;
+ // The set of packages that make-up this group.
+ std::vector<ConfiguredPackage> packages_;
+
+ // The cookies associated with each package in the group. They share the same order as
+ // packages_.
std::vector<ApkAssetsCookie> cookies_;
+
+ // A library reference table that contains build-package ID to runtime-package ID mappings.
DynamicRefTable dynamic_ref_table;
};
@@ -350,7 +382,7 @@
ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config = nullptr,
uint32_t* in_out_type_spec_flags = nullptr,
- uint32_t* out_last_ref = nullptr);
+ uint32_t* out_last_ref = nullptr) const;
private:
DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
index f281921..03fad49 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -58,6 +58,7 @@
BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
Iterator Find(uint32_t attr);
+ inline Iterator end();
private:
void JumpToClosestAttribute(uint32_t package_id);
@@ -201,6 +202,11 @@
return end_;
}
+template <typename Derived, typename Iterator>
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() {
+ return end_;
+}
+
} // namespace android
#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 69b76041..35ef98d 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -17,7 +17,8 @@
#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
#define ANDROIDFW_ATTRIBUTERESOLUTION_H
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
namespace android {
@@ -42,19 +43,19 @@
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 965e2db..35ae5fc 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,32 +41,40 @@
int package_id = 0;
};
-struct FindEntryResult {
- // A pointer to the resource table entry for this resource.
- // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
- // a ResTable_map_entry and processed as a bag/map.
- const ResTable_entry* entry = nullptr;
+// TypeSpec is going to be immediately proceeded by
+// an array of Type structs, all in the same block of memory.
+struct TypeSpec {
+ // Pointer to the mmapped data where flags are kept.
+ // Flags denote whether the resource entry is public
+ // and under which configurations it varies.
+ const ResTable_typeSpec* type_spec;
- // The configuration for which the resulting entry was defined.
- const ResTable_config* config = nullptr;
+ // Pointer to the mmapped data where the IDMAP mappings for this type
+ // exist. May be nullptr if no IDMAP exists.
+ const IdmapEntry_header* idmap_entries;
- // Stores the resulting bitmask of configuration axis with which the resource value varies.
- uint32_t type_flags = 0u;
+ // The number of types that follow this struct.
+ // There is a type for each configuration that entries are defined for.
+ size_t type_count;
- // The dynamic package ID map for the package from which this resource came from.
- const DynamicRefTable* dynamic_ref_table = nullptr;
+ // Trick to easily access a variable number of Type structs
+ // proceeding this struct, and to ensure their alignment.
+ const ResTable_type* types[0];
- // The string pool reference to the type's name. This uses a different string pool than
- // the global string pool, but this is hidden from the caller.
- StringPoolRef type_string_ref;
+ inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+ if (entry_index >= dtohl(type_spec->entryCount)) {
+ return 0u;
+ }
- // The string pool reference to the entry's name. This uses a different string pool than
- // the global string pool, but this is hidden from the caller.
- StringPoolRef entry_string_ref;
+ const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
+ return flags[entry_index];
+ }
};
-struct TypeSpec;
-class LoadedArsc;
+// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
+// ResTable_type pointers.
+// TypeSpecPtr is a managed pointer that knows how to delete itself.
+using TypeSpecPtr = util::unique_cptr<TypeSpec>;
class LoadedPackage {
public:
@@ -76,9 +84,6 @@
~LoadedPackage();
- bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
- FindEntryResult* out_entry) const;
-
// Finds the entry with the specified type name and entry name. The names are in UTF-16 because
// the underlying ResStringPool API expects this. For now this is acceptable, but since
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -86,6 +91,12 @@
// for patching the correct package ID to the resource ID.
uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
+ static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
+
+ static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
+
+ static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
+
// Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const {
return &type_string_pool_;
@@ -135,14 +146,32 @@
// before being inserted into the set. This may cause some equivalent locales to de-dupe.
void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
+ // type_idx is TT - 1 from 0xPPTTEEEE.
+ inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
+ // If the type IDs are offset in this package, we need to take that into account when searching
+ // for a type.
+ return type_specs_[type_index - type_id_offset_].get();
+ }
+
+ template <typename Func>
+ void ForEachTypeSpec(Func f) const {
+ for (size_t i = 0; i < type_specs_.size(); i++) {
+ const TypeSpecPtr& ptr = type_specs_[i];
+ if (ptr != nullptr) {
+ uint8_t type_id = ptr->type_spec->id;
+ if (ptr->idmap_entries != nullptr) {
+ type_id = ptr->idmap_entries->target_type_id;
+ }
+ f(ptr.get(), type_id - 1);
+ }
+ }
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
LoadedPackage();
- bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx,
- const ResTable_config& config, FindEntryResult* out_entry) const;
-
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
std::string package_name_;
@@ -152,7 +181,7 @@
bool system_ = false;
bool overlay_ = false;
- ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
+ ByteBucketArray<TypeSpecPtr> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
};
@@ -180,25 +209,20 @@
return &global_string_pool_;
}
- // Finds the resource with ID `resid` with the best value for configuration `config`.
- // The parameter `out_entry` will be filled with the resulting resource entry.
- // The resource entry can be a simple entry (ResTable_entry) or a complex bag
- // (ResTable_entry_map).
- bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const;
-
- // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
- const LoadedPackage* GetPackageForId(uint32_t resid) const;
-
- // Returns true if this is a system provided resource.
- inline bool IsSystem() const {
- return system_;
- }
+ // Gets a pointer to the package with the specified package ID, or nullptr if no such package
+ // exists.
+ const LoadedPackage* GetPackageById(uint8_t package_id) const;
// Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
return packages_;
}
+ // Returns true if this is a system provided resource.
+ inline bool IsSystem() const {
+ return system_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
new file mode 100644
index 0000000..64924f4
--- /dev/null
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_MUTEXGUARD_H
+#define ANDROIDFW_MUTEXGUARD_H
+
+#include <mutex>
+#include <type_traits>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+template <typename T>
+class ScopedLock;
+
+// Owns the guarded object and protects access to it via a mutex.
+// The guarded object is inaccessible via this class.
+// The mutex is locked and the object accessed via the ScopedLock<T> class.
+//
+// NOTE: The template parameter T should not be a raw pointer, since ownership
+// is ambiguous and error-prone. Instead use an std::unique_ptr<>.
+//
+// Example use:
+//
+// Guarded<std::string> shared_string("hello");
+// {
+// ScopedLock<std::string> locked_string(shared_string);
+// *locked_string += " world";
+// }
+//
+template <typename T>
+class Guarded {
+ static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
+
+ public:
+ explicit Guarded() : guarded_() {
+ }
+
+ template <typename U = T>
+ explicit Guarded(const T& guarded,
+ typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
+ : guarded_(guarded) {
+ }
+
+ template <typename U = T>
+ explicit Guarded(T&& guarded,
+ typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
+ : guarded_(std::move(guarded)) {
+ }
+
+ private:
+ friend class ScopedLock<T>;
+
+ DISALLOW_COPY_AND_ASSIGN(Guarded);
+
+ std::mutex lock_;
+ T guarded_;
+};
+
+template <typename T>
+class ScopedLock {
+ public:
+ explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+ }
+
+ T& operator*() {
+ return guarded_;
+ }
+
+ T* operator->() {
+ return &guarded_;
+ }
+
+ T* get() {
+ return &guarded_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedLock);
+
+ std::lock_guard<std::mutex> lock_;
+ T& guarded_;
+};
+
+} // namespace android
+
+#endif // ANDROIDFW_MUTEXGUARD_H
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index c2eae85..d94779b 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -28,7 +28,7 @@
StringPiece* out_entry);
inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
- return resid | (static_cast<uint32_t>(package_id) << 24);
+ return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
}
inline uint8_t get_package_id(uint32_t resid) {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 6c43a67..e2b9f00 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -26,58 +26,56 @@
using ::android::base::unique_fd;
using ::com::android::basic::R;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
namespace android {
TEST(ApkAssetsTest, LoadApk) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_NE(nullptr, loaded_arsc);
-
- const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
- ASSERT_NE(nullptr, loaded_package);
-
- std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
- ASSERT_NE(nullptr, asset);
+ ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
+ ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkFromFd) {
const std::string path = GetTestDataPath() + "/basic/basic.apk";
unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
- ASSERT_GE(fd.get(), 0);
+ ASSERT_THAT(fd.get(), Ge(0));
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_NE(nullptr, loaded_arsc);
-
- const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
- ASSERT_NE(nullptr, loaded_package);
-
- std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
- ASSERT_NE(nullptr, asset);
+ ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
+ ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
}
TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
+
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_NE(nullptr, loaded_arsc);
- ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+ ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_NE(nullptr, loaded_arsc);
- ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+ ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
}
@@ -86,19 +84,22 @@
ResTable target_table;
const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
- ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
+ ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
+ Eq(NO_ERROR));
ResTable overlay_table;
const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
- ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
+ ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
+ Eq(NO_ERROR));
util::unique_cptr<void> idmap_data;
void* temp_data;
size_t idmap_len;
- ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
- overlay_path.c_str(), &temp_data, &idmap_len));
+ ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
+ overlay_path.c_str(), &temp_data, &idmap_len),
+ Eq(NO_ERROR));
idmap_data.reset(temp_data);
TemporaryFile tf;
@@ -108,37 +109,30 @@
// Open something so that the destructor of TemporaryFile closes a valid fd.
tf.fd = open("/dev/null", O_WRONLY);
- std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path);
- ASSERT_NE(nullptr, loaded_overlay_apk);
+ ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
}
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
- {
- std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
- ASSERT_NE(nullptr, assets);
- }
+ { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
- {
- std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
- ASSERT_NE(nullptr, assets);
- }
+ { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
}
TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
- ASSERT_NE(nullptr, asset);
+ ASSERT_THAT(asset, NotNull());
off64_t start, length;
unique_fd fd(asset->openFileDescriptor(&start, &length));
- EXPECT_GE(fd.get(), 0);
+ ASSERT_THAT(fd.get(), Ge(0));
lseek64(fd.get(), start, SEEK_SET);
@@ -146,7 +140,7 @@
buffer.resize(length);
ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length));
- EXPECT_EQ("This should be uncompressed.\n\n", buffer);
+ EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
}
} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 85e8f25..437e147 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -81,17 +81,18 @@
}
BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
-static void BM_AssetManagerGetResource(benchmark::State& state) {
- GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
- basic::R::integer::number1, state);
+static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) {
+ GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state);
}
-BENCHMARK(BM_AssetManagerGetResource);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref);
-static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
- GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
- basic::R::integer::number1, state);
+static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) {
+ GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid,
+ state);
}
-BENCHMARK(BM_AssetManagerGetResourceOld);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref);
static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
GetResourceBenchmark(
@@ -196,7 +197,7 @@
static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
AssetManager assets;
if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
- false /*isSystemAssets*/)) {
+ true /*isSystemAssets*/)) {
state.SkipWithError("Failed to load assets");
return;
}
@@ -211,4 +212,44 @@
}
BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
+static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+
+ while (state.KeepRunning()) {
+ config.sdkVersion = ~config.sdkVersion;
+ assets.SetConfiguration(config);
+ }
+}
+BENCHMARK(BM_AssetManagerSetConfigurationFramework);
+
+static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
+ true /*isSystemAssets*/)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ const ResTable& table = assets.getResources(true);
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+
+ while (state.KeepRunning()) {
+ config.sdkVersion = ~config.sdkVersion;
+ assets.setConfiguration(config);
+ }
+}
+BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld);
+
} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 92462a6..7cac2b3 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -36,6 +36,10 @@
namespace lib_two = com::android::lib_two;
namespace libclient = com::android::libclient;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
namespace android {
class AssetManager2Test : public ::testing::Test {
@@ -59,11 +63,14 @@
libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
ASSERT_NE(nullptr, libclient_assets_);
- appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
+ appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
ASSERT_NE(nullptr, appaslib_assets_);
system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
ASSERT_NE(nullptr, system_assets_);
+
+ app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
+ ASSERT_THAT(app_assets_, NotNull());
}
protected:
@@ -75,6 +82,7 @@
std::unique_ptr<const ApkAssets> libclient_assets_;
std::unique_ptr<const ApkAssets> appaslib_assets_;
std::unique_ptr<const ApkAssets> system_assets_;
+ std::unique_ptr<const ApkAssets> app_assets_;
};
TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -233,6 +241,25 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+ const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
+ ASSERT_NE(nullptr, bag);
+ ASSERT_GE(bag->entry_count, 2u);
+
+ // First two attributes come from lib_one.
+ EXPECT_EQ(1, bag->entries[0].cookie);
+ EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
+ EXPECT_EQ(1, bag->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+}
+
+TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
+ AssetManager2 assetmanager;
+
+ // libclient is built with lib_one and then lib_two in order.
+ // Reverse the order to test that proper package ID re-assignment is happening.
+ assetmanager.SetApkAssets(
+ {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
ASSERT_NE(nullptr, bag);
ASSERT_GE(bag->entry_count, 2u);
@@ -446,8 +473,68 @@
assetmanager.GetResourceId("main", "layout", "com.android.basic"));
}
-TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get()});
-TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+ std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+ ASSERT_THAT(asset, NotNull());
+
+ const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+ ASSERT_THAT(data, NotNull());
+ EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n"));
+}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+ std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+ ASSERT_THAT(asset, NotNull());
+
+ const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+ ASSERT_THAT(data, NotNull());
+ EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n"));
+}
+
+TEST_F(AssetManager2Test, OpenDir) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get()});
+
+ std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+ ASSERT_THAT(asset_dir, NotNull());
+ ASSERT_THAT(asset_dir->getFileCount(), Eq(2u));
+
+ EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt")));
+ EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+ EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir")));
+ EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory));
+
+ asset_dir = assetmanager.OpenDir("subdir");
+ ASSERT_THAT(asset_dir, NotNull());
+ ASSERT_THAT(asset_dir->getFileCount(), Eq(1u));
+
+ EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt")));
+ EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+}
+
+TEST_F(AssetManager2Test, OpenDirFromManyApks) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+ std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+ ASSERT_THAT(asset_dir, NotNull());
+ ASSERT_THAT(asset_dir->getFileCount(), Eq(3u));
+
+ EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt")));
+ EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+ EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt")));
+ EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular));
+
+ EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir")));
+ EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
+}
} // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
new file mode 100644
index 0000000..fa300c5
--- /dev/null
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+//#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "BenchmarkHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace app = com::android::app;
+namespace basic = com::android::basic;
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr const static uint32_t Theme_Material_Light = 0x01030237u;
+
+static void BM_ApplyStyle(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> styles_apk =
+ ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ if (styles_apk == nullptr) {
+ state.SkipWithError("failed to load assets");
+ return;
+ }
+
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({styles_apk.get()});
+
+ std::unique_ptr<Asset> asset =
+ assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ state.SkipWithError("failed to load layout");
+ return;
+ }
+
+ ResXMLTree xml_tree;
+ if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
+ state.SkipWithError("corrupt xml layout");
+ return;
+ }
+
+ // Skip to the first tag.
+ while (xml_tree.next() != ResXMLParser::START_TAG) {
+ }
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ theme->ApplyStyle(app::R::style::StyleTwo);
+
+ std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two,
+ app::R::attr::attr_three, app::R::attr::attr_four,
+ app::R::attr::attr_five, app::R::attr::attr_empty}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> indices;
+
+ while (state.KeepRunning()) {
+ ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
+ attrs.size(), values.data(), indices.data());
+ }
+}
+BENCHMARK(BM_ApplyStyle);
+
+static void BM_ApplyStyleFramework(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath);
+ if (framework_apk == nullptr) {
+ state.SkipWithError("failed to load framework assets");
+ return;
+ }
+
+ std::unique_ptr<const ApkAssets> basic_apk =
+ ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ if (basic_apk == nullptr) {
+ state.SkipWithError("failed to load assets");
+ return;
+ }
+
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()});
+
+ ResTable_config device_config;
+ memset(&device_config, 0, sizeof(device_config));
+ device_config.language[0] = 'e';
+ device_config.language[1] = 'n';
+ device_config.country[0] = 'U';
+ device_config.country[1] = 'S';
+ device_config.orientation = ResTable_config::ORIENTATION_PORT;
+ device_config.smallestScreenWidthDp = 700;
+ device_config.screenWidthDp = 700;
+ device_config.screenHeightDp = 1024;
+ device_config.sdkVersion = 27;
+
+ Res_value value;
+ ResTable_config config;
+ uint32_t flags = 0u;
+ ApkAssetsCookie cookie =
+ assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
+ 0u /*density_override*/, &value, &config, &flags);
+ if (cookie == kInvalidCookie) {
+ state.SkipWithError("failed to find R.layout.layout");
+ return;
+ }
+
+ size_t len = 0u;
+ const char* layout_path =
+ assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
+ if (layout_path == nullptr || len == 0u) {
+ state.SkipWithError("failed to lookup layout path");
+ return;
+ }
+
+ std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
+ StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ state.SkipWithError("failed to load layout");
+ return;
+ }
+
+ ResXMLTree xml_tree;
+ if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
+ state.SkipWithError("corrupt xml layout");
+ return;
+ }
+
+ // Skip to the first tag.
+ while (xml_tree.next() != ResXMLParser::START_TAG) {
+ }
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ theme->ApplyStyle(Theme_Material_Light);
+
+ std::array<uint32_t, 92> attrs{
+ {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099,
+ 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f,
+ 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151,
+ 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158,
+ 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f,
+ 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166,
+ 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d,
+ 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d,
+ 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5,
+ 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f,
+ 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d,
+ 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df,
+ 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9,
+ 0x011100ca}};
+
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> indices;
+ while (state.KeepRunning()) {
+ ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/,
+ attrs.data(), attrs.size(), values.data(), indices.data());
+ }
+}
+BENCHMARK(BM_ApplyStyleFramework);
+
+} // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 2d73ce8..c8dbe20 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -21,6 +21,8 @@
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceUtils.h"
#include "TestHelpers.h"
#include "data/styles/R.h"
@@ -32,15 +34,14 @@
class AttributeResolutionTest : public ::testing::Test {
public:
virtual void SetUp() override {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(
- GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents));
- ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(),
- 1 /*cookie*/, true /*copyData*/));
+ styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ ASSERT_NE(nullptr, styles_assets_);
+ assetmanager_.SetApkAssets({styles_assets_.get()});
}
protected:
- ResTable table_;
+ std::unique_ptr<const ApkAssets> styles_assets_;
+ AssetManager2 assetmanager_;
};
class AttributeResolutionXmlTest : public AttributeResolutionTest {
@@ -48,13 +49,12 @@
virtual void SetUp() override {
AttributeResolutionTest::SetUp();
- std::string contents;
- ASSERT_TRUE(
- ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk",
- "res/layout/layout.xml", &contents));
+ std::unique_ptr<Asset> asset =
+ assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
+ ASSERT_NE(nullptr, asset);
- ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(),
- true /*copyData*/));
+ ASSERT_EQ(NO_ERROR,
+ xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/));
// Skip to the first tag.
while (xml_parser_.next() != ResXMLParser::START_TAG) {
@@ -65,15 +65,50 @@
ResXMLTree xml_parser_;
};
+TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
+ AssetManager2 assetmanager;
+ auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk");
+ ASSERT_NE(nullptr, apk_assets);
+ assetmanager.SetApkAssets({apk_assets.get()});
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+
+ std::array<uint32_t, 2> attrs{
+ {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> indices;
+ ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
+ fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
+ indices.data());
+
+ const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+ const uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(2u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
TEST_F(AttributeResolutionTest, Theme) {
- ResTable::Theme theme(table_);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+ std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+ ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
attrs.size(), values.data(), nullptr /*out_indices*/));
@@ -126,8 +161,8 @@
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
- nullptr /*out_indices*/));
+ ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
+ values.data(), nullptr /*out_indices*/));
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -171,15 +206,15 @@
}
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
- ResTable::Theme theme(table_);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+ std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
+ ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
attrs.size(), values.data(), indices.data());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index 7149bee..faddfe5 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -33,19 +33,21 @@
}
}
+ // Make sure to force creation of the ResTable first, or else the configuration doesn't get set.
+ const ResTable& table = assetmanager.getResources(true);
if (config != nullptr) {
assetmanager.setConfiguration(*config);
}
- const ResTable& table = assetmanager.getResources(true);
-
Res_value value;
ResTable_config selected_config;
uint32_t flags;
+ uint32_t last_ref = 0u;
while (state.KeepRunning()) {
- table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
- &selected_config);
+ ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
+ &selected_config);
+ table.resolveReference(&value, block, &last_ref, &flags, &selected_config);
}
}
@@ -72,10 +74,12 @@
Res_value value;
ResTable_config selected_config;
uint32_t flags;
+ uint32_t last_id = 0u;
while (state.KeepRunning()) {
- assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
- &selected_config, &flags);
+ ApkAssetsCookie cookie = assetmanager.GetResource(
+ resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
+ assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
}
}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 37ddafb..cae632d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,6 +16,9 @@
#include "androidfw/LoadedArsc.h"
+#include "android-base/file.h"
+#include "androidfw/ResourceUtils.h"
+
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/libclient/R.h"
@@ -27,6 +30,14 @@
namespace libclient = com::android::libclient;
namespace sparse = com::android::sparse;
+using ::android::base::ReadFileToString;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
namespace android {
TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -35,39 +46,24 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
- const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
- ASSERT_EQ(1u, packages.size());
- EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName());
- EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one));
+ ASSERT_THAT(package, NotNull());
+ EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app"));
+ EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 24;
+ const uint8_t type_index = get_type_id(app::R::string::string_one) - 1;
+ const uint16_t entry_index = get_entry_id(app::R::string::string_one);
- FindEntryResult entry;
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry));
- ASSERT_NE(nullptr, entry.entry);
-}
-
-TEST(LoadedArscTest, FindDefaultEntry) {
- std::string contents;
- ASSERT_TRUE(
- ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
-
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
-
- ResTable_config desired_config;
- memset(&desired_config, 0, sizeof(desired_config));
- desired_config.language[0] = 'd';
- desired_config.language[1] = 'e';
-
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry));
- ASSERT_NE(nullptr, entry.entry);
+ const ResTable_type* type = type_spec->types[0];
+ ASSERT_THAT(type, NotNull());
+ ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
}
TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -76,15 +72,22 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
+ ASSERT_THAT(package, NotNull());
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
- ASSERT_NE(nullptr, entry.entry);
+ const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1;
+ const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+
+ const ResTable_type* type = type_spec->types[0];
+ ASSERT_THAT(type, NotNull());
+ ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
}
TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -93,14 +96,13 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_EQ(1u, packages.size());
-
+ ASSERT_THAT(packages, SizeIs(1u));
EXPECT_TRUE(packages[0]->IsDynamic());
- EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName());
- EXPECT_EQ(0, packages[0]->GetPackageId());
+ EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one"));
+ EXPECT_THAT(packages[0]->GetPackageId(), Eq(0));
const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
@@ -114,25 +116,23 @@
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_EQ(1u, packages.size());
-
+ ASSERT_THAT(packages, SizeIs(1u));
EXPECT_FALSE(packages[0]->IsDynamic());
- EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName());
- EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+ EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient"));
+ EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
// The library has two dependencies.
- ASSERT_EQ(2u, dynamic_pkg_map.size());
+ ASSERT_THAT(dynamic_pkg_map, SizeIs(2u));
+ EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one"));
+ EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02));
- EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name);
- EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id);
-
- EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name);
- EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id);
+ EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two"));
+ EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03));
}
TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
@@ -143,13 +143,12 @@
std::unique_ptr<const LoadedArsc> loaded_arsc =
LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
true /*load_as_shared_library*/);
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_EQ(1u, packages.size());
-
+ ASSERT_THAT(packages, SizeIs(1u));
EXPECT_TRUE(packages[0]->IsDynamic());
- EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+ EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
}
TEST(LoadedArscTest, LoadFeatureSplit) {
@@ -157,21 +156,67 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
- ResTable_config desired_config;
- memset(&desired_config, 0, sizeof(desired_config));
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3));
+ ASSERT_THAT(package, NotNull());
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry));
+ uint8_t type_index = get_type_id(basic::R::string::test3) - 1;
+ uint8_t entry_index = get_entry_id(basic::R::string::test3);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+ ASSERT_THAT(type_spec->types[0], NotNull());
size_t len;
- const char16_t* type_name16 = entry.type_string_ref.string16(&len);
- ASSERT_NE(nullptr, type_name16);
- ASSERT_NE(0u, len);
+ const char16_t* type_name16 =
+ package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
+ ASSERT_THAT(type_name16, NotNull());
+ EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
- std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len));
- EXPECT_EQ(std::string("string"), type_name);
+ ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+}
+
+// AAPT(2) generates resource tables with chunks in a certain order. The rule is that
+// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with
+// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however.
+//
+// AAPT(2) generates something like:
+// RES_TABLE_TYPE_SPEC_TYPE id=1
+// RES_TABLE_TYPE_TYPE id=1
+// RES_TABLE_TYPE_SPEC_TYPE id=2
+// RES_TABLE_TYPE_TYPE id=2
+//
+// But the following is valid too:
+// RES_TABLE_TYPE_SPEC_TYPE id=1
+// RES_TABLE_TYPE_SPEC_TYPE id=2
+// RES_TABLE_TYPE_TYPE id=1
+// RES_TABLE_TYPE_TYPE id=2
+//
+TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk",
+ "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+ const auto& package = loaded_arsc->GetPackages()[0];
+ ASSERT_THAT(package, NotNull());
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+ ASSERT_THAT(type_spec->types[0], NotNull());
+
+ type_spec = package->GetTypeSpecByTypeIndex(1);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+ ASSERT_THAT(type_spec->types[0], NotNull());
}
class MockLoadedIdmap : public LoadedIdmap {
@@ -199,23 +244,33 @@
};
TEST(LoadedArscTest, LoadOverlay) {
- std::string contents, overlay_contents;
- ASSERT_TRUE(
- ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+ std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
- &overlay_contents));
+ &contents));
MockLoadedIdmap loaded_idmap;
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap);
- ASSERT_NE(nullptr, loaded_arsc);
+ LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
+ ASSERT_THAT(loaded_arsc, NotNull());
- ResTable_config desired_config;
- memset(&desired_config, 0, sizeof(desired_config));
+ const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
+ ASSERT_THAT(package, NotNull());
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+ ASSERT_THAT(type_spec->types[0], NotNull());
+
+ // The entry being overlaid doesn't exist at the original entry index.
+ ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
+
+ // Since this is an overlay, the actual entry ID must be mapped.
+ ASSERT_THAT(type_spec->idmap_entries, NotNull());
+ uint16_t target_entry_id = 0u;
+ ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
+ ASSERT_THAT(target_entry_id, Eq(0x0u));
+ ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index 43a9955..df0c642 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -20,6 +20,7 @@
#include <string>
#include "androidfw/ResourceTypes.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CommonHelpers.h"
diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk
index ccb0824..c8ad86d 100644
--- a/libs/androidfw/tests/data/app/app.apk
+++ b/libs/androidfw/tests/data/app/app.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt
new file mode 100644
index 0000000..b214e06
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/app_file.txt
@@ -0,0 +1 @@
+app file
diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt
new file mode 100644
index 0000000..0811542
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/file.txt
@@ -0,0 +1 @@
+app override file
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
index d418158..09af842 100755
--- a/libs/androidfw/tests/data/app/build
+++ b/libs/androidfw/tests/data/app/build
@@ -17,4 +17,11 @@
set -e
-aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link \
+ --manifest AndroidManifest.xml \
+ -I ../system/system.apk \
+ -A assets \
+ -o app.apk \
+ compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 94a2a14..b7e814f 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -34,6 +34,7 @@
struct layout {
enum : uint32_t {
main = 0x7f020000,
+ layoutt = 0x7f020001,
};
};
@@ -55,6 +56,7 @@
number2 = 0x7f040001,
ref1 = 0x7f040002,
ref2 = 0x7f040003,
+ deep_ref = 0x7f040004,
// From feature
number3 = 0x80030000,
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 18ef75e..1733b6a 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml
new file mode 100644
index 0000000..045ede4
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ok"
+ android:layout_width="0sp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_marginStart="2dip"
+ android:layout_marginEnd="2dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:text="@android:string/ok" />
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 6c47459..b343562 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -22,6 +22,7 @@
<attr name="attr2" format="reference|integer" />
<public type="layout" name="main" id="0x7f020000" />
+ <public type="layout" name="layout" id="0x7f020001" />
<public type="string" name="test1" id="0x7f030000" />
<string name="test1">test1</string>
@@ -43,6 +44,18 @@
<public type="integer" name="ref2" id="0x7f040003" />
<integer name="ref2">12000</integer>
+ <public type="integer" name="deep_ref" id="0x7f040004" />
+ <integer name="deep_ref">@integer/deep_ref_1</integer>
+ <integer name="deep_ref_1">@integer/deep_ref_2</integer>
+ <integer name="deep_ref_2">@integer/deep_ref_3</integer>
+ <integer name="deep_ref_3">@integer/deep_ref_4</integer>
+ <integer name="deep_ref_4">@integer/deep_ref_5</integer>
+ <integer name="deep_ref_5">@integer/deep_ref_6</integer>
+ <integer name="deep_ref_6">@integer/deep_ref_7</integer>
+ <integer name="deep_ref_7">@integer/deep_ref_8</integer>
+ <integer name="deep_ref_8">@integer/deep_ref_9</integer>
+ <integer name="deep_ref_9">100</integer>
+
<public type="style" name="Theme1" id="0x7f050000" />
<style name="Theme1">
<item name="com.android.basic:attr1">100</item>
diff --git a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml
new file mode 100644
index 0000000..34016db
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app" />
diff --git a/libs/androidfw/tests/data/out_of_order_types/build b/libs/androidfw/tests/data/out_of_order_types/build
new file mode 100755
index 0000000..8496f81
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o out_of_order_types.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt
new file mode 100644
index 0000000..eca8f47
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt
@@ -0,0 +1,43 @@
+00000000: 0200 0c00 ac02 0000 0100 0000 0100 1c00 ................
+00000010: 1c00 0000 0000 0000 0000 0000 0001 0000 ................
+00000020: 1c00 0000 0000 0000 0002 2001 8402 0000 .......... .....
+00000030: 7f00 0000 6300 6f00 6d00 2e00 6100 6e00 ....c.o.m...a.n.
+00000040: 6400 7200 6f00 6900 6400 2e00 6100 7000 d.r.o.i.d...a.p.
+00000050: 7000 0000 0000 0000 0000 0000 0000 0000 p...............
+00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000130: 0000 0000 2001 0000 0000 0000 6401 0000 .... .......d...
+00000140: 0000 0000 0000 0000 0100 1c00 4400 0000 ............D...
+00000150: 0200 0000 0000 0000 0000 0000 2400 0000 ............$...
+00000160: 0000 0000 0000 0000 0c00 0000 0400 6200 ..............b.
+00000170: 6f00 6f00 6c00 0000 0700 6900 6e00 7400 o.o.l.....i.n.t.
+00000180: 6500 6700 6500 7200 0000 0000 0100 1c00 e.g.e.r.........
+00000190: 2800 0000 0100 0000 0000 0000 0001 0000 (...............
+000001a0: 2000 0000 0000 0000 0000 0000 0404 7465 .............te
+000001b0: 7374 0000 0202 1000 1400 0000 0100 0000 st..............
+000001c0: 0100 0000 0000 0000 0202 1000 1400 0000
+000001d0: 0200 0000 0100 0000 0000 0000 0102 5400
+000001e0: 6800 0000 0100 0000 0100 0000 5800 0000
+000001f0: 4000 0000 0000 0000 0000 0000 0000 0000
+00000200: 0000 0000 0000 0000 0000 0000 0000 0000
+00000210: 0000 0000 0000 0000 0000 0000 0000 0000
+00000220: 0000 0000 0000 0000 0000 0000 0000 0000
+00000230: 0000 0000 0800 0000 0000 0000 0800 0012
+00000240: ffff ffff 0102 5400 6800 0000 0200 0000 ......T.h.......
+00000250: 0100 0000 5800 0000 4000 0000 0000 0000 ....X...@.......
+00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000270: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+00000290: 0000 0000 0000 0000 0000 0000 0800 0000 ................
+000002a0: 0000 0000 0800 0010 0100 0000 ............
diff --git a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk
new file mode 100644
index 0000000..75146e0
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml
new file mode 100644
index 0000000..7c54fba
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <bool name="test">true</bool>
+ <integer name="test">1</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/system/assets/file.txt b/libs/androidfw/tests/data/system/assets/file.txt
new file mode 100644
index 0000000..f73f309
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/file.txt
@@ -0,0 +1 @@
+file
diff --git a/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
new file mode 100644
index 0000000..3f74eb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
@@ -0,0 +1 @@
+subdir file
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
index bfbdf4c..b65145a 100755
--- a/libs/androidfw/tests/data/system/build
+++ b/libs/androidfw/tests/data/system/build
@@ -17,4 +17,6 @@
set -e
-aapt package -x -M AndroidManifest.xml -S res -F system.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o system.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
index b97bdb6..5f60d21 100644
--- a/libs/androidfw/tests/data/system/res/values-sv/values.xml
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -15,6 +15,5 @@
-->
<resources>
- <public type="integer" name="number" id="0x01030000" />
<integer name="number">1</integer>
</resources>
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
index 35d43c7..7893c94 100644
--- a/libs/androidfw/tests/data/system/res/values/themes.xml
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -18,6 +18,7 @@
<public name="background" type="attr" id="0x01010000"/>
<public name="foreground" type="attr" id="0x01010001"/>
<public name="Theme.One" type="style" id="0x01020000"/>
+ <public type="integer" name="number" id="0x01030000" />
<attr name="background" format="color|reference"/>
<attr name="foreground" format="color|reference"/>
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 1299016..9045d6c 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 98e9a42..e70d5ea 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -18,9 +18,11 @@
#include <utils/Log.h>
#include <android/asset_manager_jni.h>
+#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <utils/threads.h>
#include "jni.h"
@@ -35,21 +37,20 @@
// -----
struct AAssetDir {
- AssetDir* mAssetDir;
+ std::unique_ptr<AssetDir> mAssetDir;
size_t mCurFileIndex;
String8 mCachedFileName;
- explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { }
- ~AAssetDir() { delete mAssetDir; }
+ explicit AAssetDir(std::unique_ptr<AssetDir> dir) :
+ mAssetDir(std::move(dir)), mCurFileIndex(0) { }
};
// -----
struct AAsset {
- Asset* mAsset;
+ std::unique_ptr<Asset> mAsset;
- explicit AAsset(Asset* asset) : mAsset(asset) { }
- ~AAsset() { delete mAsset; }
+ explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { }
};
// -------------------- Public native C API --------------------
@@ -104,19 +105,18 @@
return NULL;
}
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- Asset* asset = mgr->open(filename, amMode);
- if (asset == NULL) {
- return NULL;
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode);
+ if (asset == nullptr) {
+ return nullptr;
}
-
- return new AAsset(asset);
+ return new AAsset(std::move(asset));
}
AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName)
{
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- return new AAssetDir(mgr->openDir(dirName));
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ return new AAssetDir(locked_mgr->OpenDir(dirName));
}
/**
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 77237ae..87fe9ed 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -17,9 +17,10 @@
#define LOG_TAG "Configuration"
#include <utils/Log.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <android_runtime/android_content_res_Configuration.h>
+#include <android_runtime/android_util_AssetManager.h>
using namespace android;
@@ -34,7 +35,11 @@
}
void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
- ((AssetManager*)am)->getConfiguration(out);
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am));
+ ResTable_config config = locked_mgr->GetConfiguration();
+
+ // AConfiguration is not a virtual subclass, so we can memcpy.
+ memcpy(out, &config, sizeof(config));
}
void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) {
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b32be73..52d0e08e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -24,8 +24,9 @@
#include <utils/misc.h>
#include <inttypes.h>
+#include <android-base/macros.h>
#include <androidfw/Asset.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <androidfw/ResourceTypes.h>
#include <android-base/macros.h>
@@ -1664,18 +1665,22 @@
static jlong
nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
- jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset);
+ jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release());
return id;
}
@@ -1752,22 +1757,25 @@
nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path,
jfloat fontSize, jint dpi)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con,
str.c_str(), str.length(),
fontSize, dpi,
asset->getBuffer(false), asset->getLength());
- delete asset;
return id;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 0b9412b..863045c 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -180,12 +180,14 @@
List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
final int userId) {
return selectWhereTarget(targetPackageName, userId)
+ .filter((i) -> !i.isStatic())
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.toList());
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
return selectWhereUser(userId)
+ .filter((i) -> !i.isStatic())
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
Collectors.toList()));