Refactor AssetManager

Bug: 64071469
Test: atest CtsContentTestCases
Change-Id: Ia6856157e8813856268fba003e1e591d690cb26e
This commit is contained in:
Adam Lesinski
2018-02-12 14:27:46 -08:00
parent 0e35073ec9
commit bebfcc46a2
61 changed files with 4564 additions and 3316 deletions

View File

@@ -526,41 +526,22 @@ Landroid/content/res/AssetFileDescriptor;->mLength:J
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;->mResources:Landroid/content/res/Resources;
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

View File

@@ -6366,6 +6366,8 @@ public class Activity extends ContextThemeWrapper
} else {
writer.print(prefix); writer.println("No AutofillManager");
}
ResourcesManager.getInstance().dump(prefix, writer);
}
/**

View File

@@ -21,6 +21,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION;
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.os.Trace;
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 android.view.DisplayAdjustments;
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 @@ public class ResourcesManager {
* 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 @@ public class ResourcesManager {
*/
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 @@ public class ResourcesManager {
}
}
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 @@ public class ResourcesManager {
*/
@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 @@ public class ResourcesManager {
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 @@ public class ResourcesManager {
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 @@ public class ResourcesManager {
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 @@ public class ResourcesManager {
// 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,

View File

@@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper;
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 @@ public class PackageParser {
*/
@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 @@ public class PackageParser {
}
}
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 @@ public class PackageParser {
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 @@ public class PackageParser {
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 @@ public class PackageParser {
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 @@ public class PackageParser {
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 @@ public class PackageParser {
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
IoUtils.closeQuietly(assets);
// TODO(b/72056911): Implement and call close() on ApkAssets.
}
}

View File

@@ -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 com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
import java.io.IOException;
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -33,68 +38,66 @@ import libcore.io.IoUtils;
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);
}
}

View File

@@ -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 @@ import java.util.Collections;
* 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 @@ public class SplitAssetDependencyLoader
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 @@ public class SplitAssetDependencyLoader
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);

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -590,7 +590,7 @@ public class Resources {
*/
@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 @@ public class Resources {
@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 @@ public class Resources {
// 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;

View File

@@ -170,7 +170,6 @@ public class ResourcesImpl {
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -1300,8 +1299,7 @@ public class ResourcesImpl {
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 @@ public class ResourcesImpl {
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 @@ public class ResourcesImpl {
// 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 @@ public class ResourcesImpl {
}
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 @@ public class ResourcesImpl {
@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 @@ public class ResourcesImpl {
*/
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);
}
}
}

View File

@@ -61,6 +61,15 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 @@ public class TypedArray {
}
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 @@ public class TypedArray {
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 @@ public class TypedArray {
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 */

View File

@@ -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 @@ import java.io.Reader;
*
* {@hide}
*/
final class XmlBlock {
final class XmlBlock implements AutoCloseable {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@ final class XmlBlock {
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
@Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -478,13 +480,13 @@ final class XmlBlock {
* 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;

View File

@@ -110,8 +110,8 @@ cc_library_shared {
"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 @@ cc_library_shared {
"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",

View File

@@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env);
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 @@ static const RegJNIRec gRegJNI[] = {
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),

View File

@@ -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 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b
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 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b
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);
}

View File

@@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName
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);

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -145,6 +145,7 @@ cc_test {
"tests/TypeWrappers_test.cpp",
"tests/ZipUtils_test.cpp",
],
static_libs: ["libgmock"],
target: {
android: {
srcs: [
@@ -171,6 +172,7 @@ cc_benchmark {
// Actual benchmarks.
"tests/AssetManager2_bench.cpp",
"tests/AttributeResolution_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],

View File

@@ -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<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
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<const ApkAssets> ApkAssets::LoadImpl(
}
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 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
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);

View File

@@ -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 AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets
bool invalidate_caches) {
apk_assets_ = apk_assets;
BuildDynamicRefTable();
RebuildFilterList();
if (invalidate_caches) {
InvalidateCaches(static_cast<uint32_t>(-1));
}
@@ -74,12 +100,14 @@ void AssetManager2::BuildDynamicRefTable() {
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 @@ void AssetManager2::BuildDynamicRefTable() {
// 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::BuildDynamicRefTable() {
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 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
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 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
// 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,
}
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 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
}
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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
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, &current_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 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
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 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
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 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
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 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
// 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 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
// 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::GetResource(uint32_t resid, bool may_be_bag,
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 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu
}
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 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
// 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* AssetManager2::GetBag(uint32_t resid) {
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 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
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 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
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 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) {
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 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
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 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
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 @@ struct Theme::Package {
};
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::GetAttribute(uint32_t resid, Res_value* out_value,
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);

View File

@@ -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 XmlAttributeFinder
};
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 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
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 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
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 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
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 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
block = -1;
cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
// 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 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
// 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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
}
} 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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
block = kXmlBlock;
cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
// 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 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s
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 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
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 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
out_values += STYLE_NUM_ENTRIES;
}
res->unlock();
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}

View File

@@ -44,44 +44,6 @@ namespace android {
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 @@ class TypeSpecPtrBuilder {
}
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 @@ class TypeSpecPtrBuilder {
const ResTable_typeSpec* header_;
const IdmapEntry_header* idmap_header_;
std::vector<Type> types_;
std::vector<const ResTable_type*> types_;
};
} // namespace
@@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) {
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 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset
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 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset
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 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset
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 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset
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 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset
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 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
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 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
}
}
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 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out
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 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name,
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 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name,
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 @@ const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} 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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
// 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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} 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 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
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 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
} 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 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
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 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
break;
default:
LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
}
}

View File

@@ -69,6 +69,8 @@ struct ResolvedBag {
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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
//
// 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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
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 @@ class AssetManager2 {
// 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 @@ class AssetManager2 {
// 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 @@ class Theme {
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);

View File

@@ -58,6 +58,7 @@ class BackTrackingAttributeFinder {
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 @@ Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) {
return end_;
}
template <typename Derived, typename Iterator>
Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() {
return end_;
}
} // namespace android
#endif // ANDROIDFW_ATTRIBUTE_FINDER_H

View File

@@ -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 @@ enum {
// `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

View File

@@ -41,32 +41,40 @@ class DynamicPackageEntry {
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 @@ class LoadedPackage {
~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 @@ class LoadedPackage {
// 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 @@ class LoadedPackage {
// 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 @@ class LoadedPackage {
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 @@ class LoadedArsc {
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);

View File

@@ -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

View File

@@ -28,7 +28,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
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) {

View File

@@ -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 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) {
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 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) {
// 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 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
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

View File

@@ -81,17 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) {
}
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 @@ BENCHMARK(BM_AssetManagerGetResourceLocales);
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 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
}
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

View File

@@ -36,6 +36,10 @@ namespace lib_one = com::android::lib_one;
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 @@ class AssetManager2Test : public ::testing::Test {
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 @@ class AssetManager2Test : public ::testing::Test {
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) {
@@ -228,6 +236,25 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {}
TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) {
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(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(
@@ -446,8 +473,68 @@ TEST_F(AssetManager2Test, GetResourceId) {
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

View File

@@ -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

View File

@@ -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 @@ namespace android {
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 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest {
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 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest {
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 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) {
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, XmlParser) {
}
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;

View File

@@ -33,19 +33,21 @@ void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTab
}
}
// 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 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_
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);
}
}

View File

@@ -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 basic = com::android::basic;
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 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) {
&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 @@ TEST(LoadedArscTest, LoadSparseEntryApp) {
&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 @@ TEST(LoadedArscTest, LoadSharedLibrary) {
&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 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) {
"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 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
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 @@ TEST(LoadedArscTest, LoadFeatureSplit) {
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 @@ class MockLoadedIdmap : public LoadedIdmap {
};
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

View File

@@ -20,6 +20,7 @@
#include <string>
#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CommonHelpers.h"

View File

@@ -0,0 +1 @@
app file

View File

@@ -0,0 +1 @@
app override file

View File

@@ -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

View File

@@ -34,6 +34,7 @@ struct R {
struct layout {
enum : uint32_t {
main = 0x7f020000,
layoutt = 0x7f020001,
};
};
@@ -55,6 +56,7 @@ struct R {
number2 = 0x7f040001,
ref1 = 0x7f040002,
ref2 = 0x7f040003,
deep_ref = 0x7f040004,
// From feature
number3 = 0x80030000,

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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

View File

@@ -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 ............

View File

@@ -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>

View File

@@ -0,0 +1 @@
file

View File

@@ -0,0 +1 @@
subdir file

View File

@@ -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

View File

@@ -15,6 +15,5 @@
-->
<resources>
<public type="integer" name="number" id="0x01030000" />
<integer name="number">1</integer>
</resources>

View File

@@ -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"/>

View File

@@ -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 @@ using namespace android;
// -----
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 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode)
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));
}
/**

View File

@@ -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_delete(AConfiguration* config) {
}
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) {

View File

@@ -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 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati
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 @@ static jlong
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;
}

View File

@@ -180,12 +180,14 @@ final class OverlayManagerSettings {
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()));