From 1187590da38457809dd368d4901c9c47ac5a6958 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Jan 2017 12:58:11 -0800 Subject: [PATCH] Replace AssetManager with AssetManager2 implementation Test: atest CtsContentTestCases:android.content.res.cts Test: make libandroidfw_tests Change-Id: I2bb6d7656d2516d371e83e541ed02f91405f6d94 --- config/hiddenapi-light-greylist.txt | 16 - core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 ++ core/java/android/content/res/AssetManager.java | 1344 +++++---- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 + core/jni/android_util_AssetManager.cpp | 2892 +++++++++----------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 - libs/androidfw/include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 26 files changed, 2903 insertions(+), 2517 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 58936fcc4452..c173c2c515bc 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -220,28 +220,13 @@ Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/conte Landroid/content/pm/UserInfo;->id:I Landroid/content/pm/UserInfo;->isPrimary()Z Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -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;->getResourceBagText(II)Ljava/lang/CharSequence; -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;->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;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; @@ -271,7 +256,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;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SyncStatusInfo;->lastSuccessTime:J diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2da893771d94..258602ffa232 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.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; @@ -1596,21 +1597,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; + ApkAssets apkAssets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + 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) { @@ -1637,7 +1636,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(apkAssets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..b087c48d8d4c --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,221 @@ +/* + * 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 implements AutoCloseable { + @GuardedBy("this") private 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*/); + } + + @NonNull String getAssetPath() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + ensureValidLocked(); + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT 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) { + ensureValidLocked(); + 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) { + ensureValidLocked(); + return nativeIsUpToDate(mNativePtr); + } + } + + /** + * Closes the ApkAssets and destroys the underlying native implementation. Further use of the + * ApkAssets object will cause exceptions to be thrown. + * + * Calling close on an already closed ApkAssets does nothing. + */ + @Override + public void close() { + synchronized (this) { + if (mNativePtr == 0) { + return; + } + + mStringBlock = null; + nativeDestroy(mNativePtr); + mNativePtr = 0; + } + } + + @Override + protected void finalize() throws Throwable { + if (mNativePtr != 0) { + nativeDestroy(mNativePtr); + } + } + + private void ensureValidLocked() { + if (mNativePtr == 0) { + throw new RuntimeException("ApkAssets is closed"); + } + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd4..2db5c6b13200 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,9 +18,11 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -28,10 +30,20 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import java.io.FileDescriptor; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -42,7 +54,17 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -65,87 +87,300 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); - if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + synchronized (this) { + ensureValidLocked(); + mApkAssets = apkAssets; + nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + ensureValidLocked(); + return mApkAssets; + } + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); + newApkAssets[count] = assets; + setApkAssets(newApkAssets, true); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + private void ensureOpenLocked() { + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } } @@ -156,8 +391,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -172,15 +406,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -189,52 +423,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -244,26 +486,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -277,73 +541,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -784,7 +1094,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -794,131 +1109,57 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - - /** - * {@hide} - */ - public native static final String getAssetAllocations(); - /** - * {@hide} + * @hide */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); - - private final void incRefsLocked(long id) { + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } + + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -926,16 +1167,117 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private final void decRefsLocked(long id) { + + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd961..8f58891ed556 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -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; @@ -1789,8 +1789,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; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 97cb78bc4243..2a4b278bdca8 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,7 +168,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,8 +1273,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); } @@ -1284,7 +1282,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()); @@ -1307,12 +1305,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; } } @@ -1329,7 +1325,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; @@ -1349,14 +1345,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); } } @@ -1385,13 +1381,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); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -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 */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ 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; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 33f80ce8ffb0..78a3e137ccb0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -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", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d20217386b1e..4a032c4bbae4 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.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), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 937b3ffb9d60..f6223fad3dcb 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -217,7 +217,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -229,27 +230,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(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock 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(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 data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -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); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..c0f151b71c93 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,150 @@ +/* + * 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 "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.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; + } + + std::unique_ptr 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(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; + } + + 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 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(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(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(ptr); + std::unique_ptr 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 xml_tree = util::make_unique(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(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec3..c623ca621292 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1441 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" -#include "utils/misc.h" #include "utils/String8.h" +#include "utils/misc.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; -} - -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + std::vector all_file_paths; + { + StringPiece normalized_path = path_utf8.c_str(); + if (normalized_path.data()[0] == '/') { + normalized_path = normalized_path.substr(1); + } + std::string root_path = StringPrintf("assets/%s", normalized_path.data()); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + for (const ApkAssets* assets : assetmanager->GetApkAssets()) { + assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { + if (type == FileType::kFileTypeRegular) { + all_file_paths.push_back(file_path.to_string()); + } + }); } + } - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + jsize index = 0; + for (const std::string& file_path : all_file_paths) { + jstring java_string = env->NewStringUTF(file_path.c_str()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, index++, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} - -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#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* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,8 +265,6 @@ std::unique_ptr 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(); - // Might use this if density_override != 0. ResTable_config density_override_config; @@ -429,9 +427,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu 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); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#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(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { 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(res.getTableCookie(block)) - : static_cast(-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(res.getTableCookie(block)) - : static_cast(-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(res->getTableCookie(block)) - : static_cast(-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; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,8 +324,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i 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_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -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::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#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 diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,16 +45,17 @@ 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; + const ResTable_entry* entry; - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; + // 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 = nullptr; + 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. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +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 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 shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& 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 lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..cc3053798e7b 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,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 styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,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 = + 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) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array 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 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array 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 +170,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 = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array 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 values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..a8abcb5df86c 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& 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; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944d..e70d5ea0d566 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr 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(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr 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(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533b..52d0e08e4e7f 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include #include +#include #include -#include +#include #include #include @@ -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* 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; + { + ScopedLock 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* 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; + { + ScopedLock 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; } -- 2.11.0