Refactor AssetManager
Bug: 64071469 Test: atest CtsContentTestCases Change-Id: Ia6856157e8813856268fba003e1e591d690cb26e
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -6366,6 +6366,8 @@ public class Activity extends ContextThemeWrapper
|
||||
} else {
|
||||
writer.print(prefix); writer.println("No AutofillManager");
|
||||
}
|
||||
|
||||
ResourcesManager.getInstance().dump(prefix, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
195
core/java/android/content/res/ApkAssets.java
Normal file
195
core/java/android/content/res/ApkAssets.java
Normal 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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
157
core/jni/android_content_res_ApkAssets.cpp
Normal file
157
core/jni/android_content_res_ApkAssets.cpp
Normal 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
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ¤t_entry)) {
|
||||
|
||||
ApkAssetsCookie best_cookie = kInvalidCookie;
|
||||
const LoadedPackage* best_package = nullptr;
|
||||
const ResTable_type* best_type = nullptr;
|
||||
const ResTable_config* best_config = nullptr;
|
||||
ResTable_config best_config_copy;
|
||||
uint32_t best_offset = 0u;
|
||||
uint32_t type_flags = 0u;
|
||||
|
||||
// If desired_config is the same as the set configuration, then we can use our filtered list
|
||||
// and we don't need to match the configurations, since they already matched.
|
||||
const bool use_fast_path = desired_config == &configuration_;
|
||||
|
||||
for (size_t pi = 0; pi < package_count; pi++) {
|
||||
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
|
||||
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
|
||||
ApkAssetsCookie cookie = package_group.cookies_[pi];
|
||||
|
||||
// If the type IDs are offset in this package, we need to take that into account when searching
|
||||
// for a type.
|
||||
const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
|
||||
if (UNLIKELY(type_spec == nullptr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cumulated_flags |= current_entry.type_flags;
|
||||
uint16_t local_entry_idx = entry_idx;
|
||||
|
||||
const ResTable_config* current_config = current_entry.config;
|
||||
const ResTable_config* best_config = best_entry.config;
|
||||
if (best_cookie == kInvalidCookie ||
|
||||
current_config->isBetterThan(*best_config, desired_config) ||
|
||||
(loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) {
|
||||
best_entry = current_entry;
|
||||
best_cookie = package_group.cookies_[i];
|
||||
if (stop_at_first_match) {
|
||||
break;
|
||||
// If there is an IDMAP supplied with this package, translate the entry ID.
|
||||
if (type_spec->idmap_entries != nullptr) {
|
||||
if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
|
||||
// There is no mapping, so the resource is not meant to be in this overlay package.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
|
||||
|
||||
// If the package is an overlay, then even configurations that are the same MUST be chosen.
|
||||
const bool package_is_overlay = loaded_package->IsOverlay();
|
||||
|
||||
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
|
||||
if (use_fast_path) {
|
||||
const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
|
||||
const size_t type_count = candidate_configs.size();
|
||||
for (uint32_t i = 0; i < type_count; i++) {
|
||||
const ResTable_config& this_config = candidate_configs[i];
|
||||
|
||||
// We can skip calling ResTable_config::match() because we know that all candidate
|
||||
// configurations that do NOT match have been filtered-out.
|
||||
if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
|
||||
(package_is_overlay && this_config.compare(*best_config) == 0)) {
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const ResTable_type* type_chunk = filtered_group.types[i];
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
|
||||
if (offset == ResTable_type::NO_ENTRY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
best_cookie = cookie;
|
||||
best_package = loaded_package;
|
||||
best_type = type_chunk;
|
||||
best_config = &this_config;
|
||||
best_offset = offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is the slower path, which doesn't use the filtered list of configurations.
|
||||
// Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
|
||||
// and fill in any new fields that did not exist when the APK was compiled.
|
||||
// Furthermore when selecting configurations we can't just record the pointer to the
|
||||
// ResTable_config, we must copy it.
|
||||
const auto iter_end = type_spec->types + type_spec->type_count;
|
||||
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
|
||||
ResTable_config this_config;
|
||||
this_config.copyFromDtoH((*iter)->config);
|
||||
|
||||
if (this_config.match(*desired_config)) {
|
||||
if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
|
||||
(package_is_overlay && this_config.compare(*best_config) == 0)) {
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
|
||||
if (offset == ResTable_type::NO_ENTRY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
best_cookie = cookie;
|
||||
best_package = loaded_package;
|
||||
best_type = *iter;
|
||||
best_config_copy = this_config;
|
||||
best_config = &best_config_copy;
|
||||
best_offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_cookie == kInvalidCookie) {
|
||||
if (UNLIKELY(best_cookie == kInvalidCookie)) {
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
*out_entry = best_entry;
|
||||
const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
|
||||
if (UNLIKELY(best_entry == nullptr)) {
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
out_entry->entry = best_entry;
|
||||
out_entry->config = *best_config;
|
||||
out_entry->type_flags = type_flags;
|
||||
out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
|
||||
out_entry->entry_string_ref =
|
||||
StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
|
||||
out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
|
||||
out_entry->type_flags = cumulated_flags;
|
||||
return best_cookie;
|
||||
}
|
||||
|
||||
bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
|
||||
ATRACE_CALL();
|
||||
|
||||
bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
|
||||
FindEntryResult entry;
|
||||
ApkAssetsCookie cookie =
|
||||
FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
|
||||
@@ -341,7 +462,8 @@ 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
101
libs/androidfw/include/androidfw/MutexGuard.h
Normal file
101
libs/androidfw/include/androidfw/MutexGuard.h
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
175
libs/androidfw/tests/AttributeResolution_bench.cpp
Normal file
175
libs/androidfw/tests/AttributeResolution_bench.cpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "CommonHelpers.h"
|
||||
|
||||
Binary file not shown.
1
libs/androidfw/tests/data/app/assets/app_file.txt
Normal file
1
libs/androidfw/tests/data/app/assets/app_file.txt
Normal file
@@ -0,0 +1 @@
|
||||
app file
|
||||
1
libs/androidfw/tests/data/app/assets/file.txt
Normal file
1
libs/androidfw/tests/data/app/assets/file.txt
Normal file
@@ -0,0 +1 @@
|
||||
app override 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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Binary file not shown.
25
libs/androidfw/tests/data/basic/res/layout/layout.xml
Normal file
25
libs/androidfw/tests/data/basic/res/layout/layout.xml
Normal 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" />
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
22
libs/androidfw/tests/data/out_of_order_types/build
Executable file
22
libs/androidfw/tests/data/out_of_order_types/build
Executable 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
|
||||
@@ -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 ............
|
||||
Binary file not shown.
@@ -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>
|
||||
1
libs/androidfw/tests/data/system/assets/file.txt
Normal file
1
libs/androidfw/tests/data/system/assets/file.txt
Normal file
@@ -0,0 +1 @@
|
||||
file
|
||||
@@ -0,0 +1 @@
|
||||
subdir 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
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="integer" name="number" id="0x01030000" />
|
||||
<integer name="number">1</integer>
|
||||
</resources>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Binary file not shown.
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
Reference in New Issue
Block a user