Replace AssetManager with AssetManager2 implementation
Test: atest CtsContentTestCases:android.content.res.cts
Test: make libandroidfw_tests
Change-Id: I572eb13c6a4372c7f656f5912821cececd5bf3d4
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4efd081..6805bb8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -1575,21 +1576,19 @@
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) {
@@ -1615,7 +1614,7 @@
"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 0000000..b087c48
--- /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
+ * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
+ * dynamically assigned runtime package IDs.
+ *
+ * @param fileName The path to the file within the APK.
+ * @return An XmlResourceParser.
+ * @throws IOException if the file was not found or an error occurred retrieving it.
+ */
+ public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
+ synchronized (this) {
+ 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 7866560..78370f4 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -18,9 +18,11 @@
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,18 @@
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 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 +52,17 @@
* 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,146 +85,246 @@
*/
public static final int ACCESS_BUFFER = 3;
- private static final String TAG = "AssetManager";
- private static final boolean localLOGV = false || false;
-
- private static final boolean DEBUG_REFS = false;
-
- private static final Object sSync = new Object();
- /*package*/ static AssetManager sSystem = null;
+ @GuardedBy("this") private final TypedValue mValue = new TypedValue();
+ @GuardedBy("this") private final long[] mOffsets = new long[2];
- private final TypedValue mValue = new TypedValue();
- private final long[] mOffsets = new long[2];
-
- // For communication with native code.
- private long mObject;
+ // Pointer to native implementation, stuffed inside a long.
+ @GuardedBy("this") private long mObject;
- private StringBlock mStringBlocks[] = null;
-
- private int mNumRefs = 1;
- private boolean mOpen = true;
- private HashMap<Long, RuntimeException> mRefStacks;
-
+ // The loaded asset paths.
+ @GuardedBy("this") private ApkAssets[] mApkAssets;
+
+ // Debug/reference counting implementation.
+ @GuardedBy("this") private boolean mOpen = true;
+ @GuardedBy("this") private int mNumRefs = 1;
+ @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
- * {@hide}
+ * @hide
*/
public AssetManager() {
- synchronized (this) {
- if (DEBUG_REFS) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
- }
- init(false);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
- ensureSystemAssets();
+ final ApkAssets[] assets;
+ synchronized (sSync) {
+ createSystemAssetsInZygoteLocked();
+ assets = sSystemApkAssets;
+ }
+
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
+ }
+
+ // Always set the framework resources.
+ setApkAssets(assets, false /*invalidateCaches*/);
+ }
+
+ /**
+ * Private constructor that doesn't call ensureSystemAssets.
+ * Used for the creation of system assets.
+ */
+ @SuppressWarnings("unused")
+ private AssetManager(boolean sentinel) {
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
}
}
- private static void ensureSystemAssets() {
- synchronized (sSync) {
- if (sSystem == null) {
- AssetManager system = new AssetManager(true);
- system.makeStringBlocks(null);
- sSystem = system;
- }
+ /**
+ * This must be called from Zygote so that system assets are shared by all applications.
+ * @hide
+ */
+ private static void createSystemAssetsInZygoteLocked() {
+ if (sSystem != null) {
+ return;
}
- }
-
- private AssetManager(boolean isSystem) {
- if (DEBUG_REFS) {
- synchronized (this) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
+
+ // Make sure that all IDMAPs are up to date.
+ nativeVerifySystemIdmaps();
+
+ try {
+ ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
+
+ // Load all static RROs.
+ try (FileInputStream fis = new FileInputStream(
+ "/data/resource-cache/overlays.list");
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
+ // Acquire a lock so that any idmap scanning doesn't impact the current set.
+ try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE,
+ true /*shared*/)) {
+ for (String line; (line = br.readLine()) != null; ) {
+ String idmapPath = line.split(" ")[1];
+ apkAssets.add(
+ ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
+ }
}
+
+ 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);
}
- 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());
- }
- }
- }
-
- /**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @return the string value, or {@code null}
- */
- @Nullable
- final CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
- final TypedValue outValue = mValue;
- if (getResourceValue(resId, 0, outValue, true)) {
- return outValue.coerceToString();
+ if (!mOpen) {
+ return;
}
- return null;
+
+ mOpen = false;
+ decRefsLocked(hashCode());
}
}
/**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
+ * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
+ * family of methods.
*
- * @param resId the resource identifier to load
- * @param bagEntryId
- * @return the string value, or {@code null}
+ * @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
*/
- @Nullable
- final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
+ Preconditions.checkNotNull(apkAssets, "apkAssets");
synchronized (this) {
- final TypedValue outValue = mValue;
- final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
- if (block < 0) {
- return null;
+ ensureValidLocked();
+ mApkAssets = apkAssets;
+ nativeSetApkAssets(mObject, apkAssets, invalidateCaches);
+ if (invalidateCaches) {
+ // Invalidate all caches.
+ invalidateCachesLocked(-1);
}
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- return mStringBlocks[block].get(outValue.data);
- }
- return outValue.coerceToString();
}
}
/**
- * Retrieves the string array associated with a particular resource
- * identifier for the current configuration.
+ * Invalidates the caches in this AssetManager according to the bitmask `diff`.
*
- * @param resId the resource identifier of the string array
- * @return the string array, or {@code null}
+ * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
+ * @see ActivityInfo.Config
*/
- @Nullable
- final String[] getResourceStringArray(@ArrayRes int resId) {
- return getArrayStringResource(resId);
+ 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");
+ }
}
/**
@@ -219,11 +339,14 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+ boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
- if (block < 0) {
+ ensureValidLocked();
+ final int cookie = nativeGetResourceValue(
+ mObject, resId, (short) densityDpi, outValue, resolveRefs);
+ if (cookie <= 0) {
return false;
}
@@ -232,38 +355,156 @@
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mStringBlocks[block].get(outValue.data);
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
return true;
}
}
/**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceText(@StringRes int resId) {
+ synchronized (this) {
+ final TypedValue outValue = mValue;
+ if (getResourceValue(resId, 0, outValue, true)) {
+ return outValue.coerceToString();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @param bagEntryId the index into the bag to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ synchronized (this) {
+ ensureValidLocked();
+ final TypedValue outValue = mValue;
+ final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+ if (cookie <= 0) {
+ return null;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return outValue.coerceToString();
+ }
+ }
+
+ int getResourceArraySize(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArraySize(mObject, resId);
+ }
+ }
+
+ /**
+ * Populates `outData` with array elements of `resId`. `outData` is normally
+ * used with
+ * {@link TypedArray}.
+ *
+ * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
+ * long,
+ * with the indices of the data representing the type, value, asset cookie,
+ * resource ID,
+ * configuration change mask, and density of the element.
+ *
+ * @param resId The resource ID of an array resource.
+ * @param outData The array to populate with data.
+ * @return The length of the array.
+ *
+ * @see TypedArray#STYLE_TYPE
+ * @see TypedArray#STYLE_DATA
+ * @see TypedArray#STYLE_ASSET_COOKIE
+ * @see TypedArray#STYLE_RESOURCE_ID
+ * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
+ * @see TypedArray#STYLE_DENSITY
+ */
+ int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+ Preconditions.checkNotNull(outData, "outData");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArray(mObject, resId, outData);
+ }
+ }
+
+ /**
+ * Retrieves the string array associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier of the string array
+ * @return the string array, or {@code null}
+ */
+ @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceStringArray(mObject, resId);
+ }
+ }
+
+ /**
* Retrieve the text array associated with a particular resource
* identifier.
*
* @param resId the resource id of the string array
*/
- final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+ @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
synchronized (this) {
- final int[] rawInfoArray = getArrayStringInfo(resId);
+ ensureValidLocked();
+ final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
if (rawInfoArray == null) {
return null;
}
+
final int rawInfoArrayLen = rawInfoArray.length;
final int infoArrayLen = rawInfoArrayLen / 2;
- int block;
- int index;
final CharSequence[] retArray = new CharSequence[infoArrayLen];
for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
- block = rawInfoArray[i];
- index = rawInfoArray[i + 1];
- retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ int cookie = rawInfoArray[i];
+ int index = rawInfoArray[i + 1];
+ retArray[j] = (index >= 0 && cookie > 0)
+ ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
}
return retArray;
}
}
+ @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceIntArray(mObject, resId);
+ }
+ }
+
+ /**
+ * Get the attributes for a style resource. These are the <item>
+ * elements in
+ * a <style> resource.
+ * @param resId The resource ID of the style
+ * @return An array of attribute IDs.
+ */
+ @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetStyleAttributes(mObject, resId);
+ }
+ }
+
/**
* Populates {@code outValue} with the data associated with a particular
* resource identifier for the current configuration. Resolves theme
@@ -277,73 +518,88 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+ boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
boolean resolveRefs) {
- final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
- if (block < 0) {
- return false;
- }
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- final StringBlock[] blocks = ensureStringBlocks();
- outValue.string = blocks[block].get(outValue.data);
- }
- return true;
- }
-
- /**
- * Ensures the string blocks are loaded.
- *
- * @return the string blocks
- */
- @NonNull
- final StringBlock[] ensureStringBlocks() {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- if (mStringBlocks == null) {
- makeStringBlocks(sSystem.mStringBlocks);
+ ensureValidLocked();
+ final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+ resolveRefs);
+ if (cookie <= 0) {
+ return false;
}
- return mStringBlocks;
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return true;
}
}
- /*package*/ final void makeStringBlocks(StringBlock[] seed) {
- final int seedNum = (seed != null) ? seed.length : 0;
- final int num = getStringBlockCount();
- mStringBlocks = new StringBlock[num];
- if (localLOGV) Log.v(TAG, "Making string blocks for " + this
- + ": " + num);
- for (int i=0; i<num; i++) {
- if (i < seedNum) {
- mStringBlocks[i] = seed[i];
- } else {
- mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
- }
- }
- }
-
- /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
+ void dumpTheme(long theme, int priority, String tag, String prefix) {
synchronized (this) {
- // Cookies map to string blocks starting at 1.
- return mStringBlocks[cookie - 1].get(id);
+ ensureValidLocked();
+ nativeThemeDump(mObject, theme, priority, tag, prefix);
}
}
+ @Nullable String getResourceName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourcePackageName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourcePackageName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceTypeName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceTypeName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceEntryName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceEntryName(mObject, resId);
+ }
+ }
+
+ @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+ @Nullable String defPackage) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+ }
+ }
+
+ CharSequence getPooledStringForCookie(int cookie, int id) {
+ // Cookies map to ApkAssets starting at 1.
+ return getApkAssets()[cookie - 1].getStringFromPool(id);
+ }
+
/**
* Open an asset using ACCESS_STREAMING mode. This provides access to
* files that have been bundled with an application as assets -- that is,
* files placed in to the "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
*
* @see #open(String, int)
* @see #list
*/
- public final InputStream open(String fileName) throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName) throws IOException {
return open(fileName, ACCESS_STREAMING);
}
@@ -353,8 +609,7 @@
* with an application as assets -- that is, files placed in to the
* "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
* @param accessMode Desired access mode for retrieving the data.
*
* @see #ACCESS_UNKNOWN
@@ -364,34 +619,40 @@
* @see #open(String)
* @see #list
*/
- public final InputStream open(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- long asset = openAsset(fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
- public final AssetFileDescriptor openFd(String fileName)
- throws IOException {
+ /**
+ * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides access to files that have been bundled with an application as assets -- that
+ * is, files placed in to the "assets" directory.
+ *
+ * The asset must be uncompressed, or an exception will be thrown.
+ *
+ * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @return An open AssetFileDescriptor.
+ */
+ public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
/**
@@ -406,90 +667,121 @@
*
* @see #open
*/
- public native final String[] list(String path)
- throws IOException;
+ public @Nullable String[] list(@NonNull String path) throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeList(mObject, path);
+ }
+ }
/**
- * {@hide}
* Open a non-asset file as an asset using ACCESS_STREAMING mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ *
* @see #open(String)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName) throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
return openNonAsset(0, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset file as an asset using a specific access mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ * @param accessMode Desired access mode for retrieving the data.
+ *
+ * @see #ACCESS_UNKNOWN
+ * @see #ACCESS_STREAMING
+ * @see #ACCESS_RANDOM
+ * @see #ACCESS_BUFFER
* @see #open(String, int)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+ throws IOException {
return openNonAsset(0, fileName, accessMode);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+ throws IOException {
return openNonAsset(cookie, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
* @param accessMode Desired access mode for retrieving the data.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- long asset = openNonAssetNative(cookie, fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- public final AssetFileDescriptor openNonAssetFd(String fileName)
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
throws IOException {
return openNonAssetFd(0, fileName);
}
-
- public final AssetFileDescriptor openNonAssetFd(int cookie,
- String fileName) throws IOException {
+
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd =
+ nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
- fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
/**
@@ -497,7 +789,7 @@
*
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(String fileName)
+ public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
throws IOException {
return openXmlResourceParser(0, fileName);
}
@@ -508,270 +800,265 @@
* @param cookie Identifier of the package to be opened.
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(int cookie,
- String fileName) throws IOException {
- XmlBlock block = openXmlBlockAsset(cookie, fileName);
- XmlResourceParser rp = block.newParser();
- block.close();
- return rp;
+ public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+ throws IOException {
+ try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+ XmlResourceParser parser = block.newParser();
+ // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+ // a valid native pointer, which makes newParser always return non-null. But let's
+ // be paranoid.
+ if (parser == null) {
+ throw new AssertionError("block.newParser() returned a null parser");
+ }
+ return parser;
+ }
}
/**
- * {@hide}
- * Retrieve a non-asset as a compiled XML file. Not for use by
- * applications.
+ * Retrieve a non-asset as a compiled XML file. Not for use by applications.
*
* @param fileName The name of the file to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
return openXmlBlockAsset(0, fileName);
}
/**
- * {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
* applications.
*
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ if (xmlBlock == 0) {
+ throw new FileNotFoundException("Asset XML file: " + fileName);
}
- long xmlBlock = openXmlAssetNative(cookie, fileName);
- if (xmlBlock != 0) {
- XmlBlock res = new XmlBlock(this, xmlBlock);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final XmlBlock block = new XmlBlock(this, xmlBlock);
+ incRefsLocked(block.hashCode());
+ return block;
}
- throw new FileNotFoundException("Asset XML file: " + fileName);
}
- /*package*/ void xmlBlockGone(int id) {
+ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
- /*package*/ final long createTheme() {
+ void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+ long outIndicesAddress) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long res = newTheme();
- incRefsLocked(res);
- return res;
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+ outIndicesAddress);
}
}
- /*package*/ final void releaseTheme(long theme) {
+ boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+ @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
synchronized (this) {
- deleteTheme(theme);
- decRefsLocked(theme);
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeResolveAttrs(mObject,
+ themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
}
}
+ boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+ @NonNull int[] outValues, @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(parser, "parser");
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeRetrieveAttributes(
+ mObject, parser.mParseState, inAttrs, outValues, outIndices);
+ }
+ }
+
+ long createTheme() {
+ synchronized (this) {
+ ensureValidLocked();
+ long themePtr = nativeThemeCreate(mObject);
+ incRefsLocked(themePtr);
+ return themePtr;
+ }
+ }
+
+ void releaseTheme(long themePtr) {
+ synchronized (this) {
+ nativeThemeDestroy(themePtr);
+ decRefsLocked(themePtr);
+ }
+ }
+
+ void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeThemeApplyStyle(mObject, themePtr, resId, force);
+ }
+ }
+
+ @Override
protected void finalize() throws Throwable {
- try {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this
- + " finalized with non-zero refs: " + mNumRefs);
- if (mRefStacks != null) {
- for (RuntimeException e : mRefStacks.values()) {
- Log.w(TAG, "Reference from here", e);
- }
+ if (DEBUG_REFS && mNumRefs != 0) {
+ Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+ if (mRefStacks != null) {
+ for (RuntimeException e : mRefStacks.values()) {
+ Log.w(TAG, "Reference from here", e);
}
}
- destroy();
- } finally {
- super.finalize();
+ }
+
+ if (mObject != 0) {
+ nativeDestroy(mObject);
}
}
-
+
+ /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+ safe and it does not rely on AssetManager once it has been created. It completely owns the
+ underlying Asset. */
public final class AssetInputStream extends InputStream {
+ private long mAssetNativePtr;
+ private long mLength;
+ private long mMarkPos;
+
/**
* @hide
*/
public final int getAssetInt() {
throw new UnsupportedOperationException();
}
+
/**
* @hide
*/
public final long getNativeAsset() {
- return mAsset;
+ return mAssetNativePtr;
}
- private AssetInputStream(long asset)
- {
- mAsset = asset;
- mLength = getAssetLength(asset);
+
+ private AssetInputStream(long assetNativePtr) {
+ mAssetNativePtr = assetNativePtr;
+ mLength = nativeAssetGetLength(assetNativePtr);
}
+
+ @Override
public final int read() throws IOException {
- return readAssetChar(mAsset);
+ ensureOpen();
+ return nativeAssetReadChar(mAssetNativePtr);
}
- public final boolean markSupported() {
- return true;
+
+ @Override
+ public final int read(@NonNull byte[] b) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
}
- public final int available() throws IOException {
- long len = getAssetRemainingLength(mAsset);
- return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
+
+ @Override
+ public final int read(@NonNull byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ Preconditions.checkNotNull(b, "b");
+ return nativeAssetRead(mAssetNativePtr, b, off, len);
}
- public final void close() throws IOException {
- synchronized (AssetManager.this) {
- if (mAsset != 0) {
- destroyAsset(mAsset);
- mAsset = 0;
- decRefsLocked(hashCode());
- }
- }
- }
- public final void mark(int readlimit) {
- mMarkPos = seekAsset(mAsset, 0, 0);
- }
- public final void reset() throws IOException {
- seekAsset(mAsset, mMarkPos, -1);
- }
- public final int read(byte[] b) throws IOException {
- return readAsset(mAsset, b, 0, b.length);
- }
- public final int read(byte[] b, int off, int len) throws IOException {
- return readAsset(mAsset, b, off, len);
- }
+
+ @Override
public final long skip(long n) throws IOException {
- long pos = seekAsset(mAsset, 0, 0);
- if ((pos+n) > mLength) {
- n = mLength-pos;
+ ensureOpen();
+ long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ if ((pos + n) > mLength) {
+ n = mLength - pos;
}
if (n > 0) {
- seekAsset(mAsset, n, 0);
+ nativeAssetSeek(mAssetNativePtr, n, 0);
}
return n;
}
- protected void finalize() throws Throwable
- {
+ @Override
+ public final int available() throws IOException {
+ ensureOpen();
+ final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
+ }
+
+ @Override
+ public final boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public final void mark(int readlimit) {
+ ensureOpen();
+ mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ }
+
+ @Override
+ public final void reset() throws IOException {
+ ensureOpen();
+ nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
+ }
+
+ @Override
+ public final void close() throws IOException {
+ if (mAssetNativePtr != 0) {
+ nativeAssetDestroy(mAssetNativePtr);
+ mAssetNativePtr = 0;
+
+ synchronized (AssetManager.this) {
+ decRefsLocked(hashCode());
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
close();
}
- private long mAsset;
- private long mLength;
- private long mMarkPos;
- }
-
- /**
- * Add an additional set of assets to the asset manager. This can be
- * either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPath(String path) {
- return addAssetPathInternal(path, false);
- }
-
- /**
- * Add an application assets to the asset manager and loading it as shared library.
- * This can be either a directory or ZIP file. Not for use by applications. Returns
- * the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public final int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, true);
- }
-
- private final int addAssetPathInternal(String path, boolean appAsLib) {
- synchronized (this) {
- int res = addAssetPathNative(path, appAsLib);
- makeStringBlocks(mStringBlocks);
- return res;
+ private void ensureOpen() {
+ if (mAssetNativePtr == 0) {
+ throw new IllegalStateException("AssetInputStream is closed");
+ }
}
}
- private native final int addAssetPathNative(String path, boolean appAsLib);
-
- /**
- * Add an additional set of assets to the asset manager from an already open
- * FileDescriptor. Not for use by applications.
- * This does not give full AssetManager functionality for these assets,
- * since the origin of the file is not known for purposes of sharing,
- * overlay resolution, and other features. However it does allow you
- * to do simple access to the contents of the given fd as an apk file.
- * Performs a dup of the underlying fd, so you must take care of still closing
- * the FileDescriptor yourself (and can do that whenever you want).
- * Returns the cookie of the added asset, or 0 on failure.
- * {@hide}
- */
- public int addAssetFd(FileDescriptor fd, String debugPathName) {
- return addAssetFdInternal(fd, debugPathName, false);
- }
-
- private int addAssetFdInternal(FileDescriptor fd, String debugPathName,
- boolean appAsLib) {
- synchronized (this) {
- int res = addAssetFdNative(fd, debugPathName, appAsLib);
- makeStringBlocks(mStringBlocks);
- return res;
- }
- }
-
- private native int addAssetFdNative(FileDescriptor fd, String debugPathName,
- boolean appAsLib);
-
- /**
- * Add a set of assets to overlay an already added set of assets.
- *
- * This is only intended for application resources. System wide resources
- * are handled before any Java code is executed.
- *
- * {@hide}
- */
-
- public final int addOverlayPath(String idmapPath) {
- synchronized (this) {
- int res = addOverlayPathNative(idmapPath);
- makeStringBlocks(mStringBlocks);
- return res;
- }
- }
-
- /**
- * See addOverlayPath.
- *
- * {@hide}
- */
- public native final int addOverlayPathNative(String idmapPath);
-
- /**
- * Add multiple sets of assets to the asset manager at once. See
- * {@link #addAssetPath(String)} for more information. Returns array of
- * cookies for each added asset with 0 indicating failure, or null if
- * the input array of paths is null.
- * {@hide}
- */
- public final int[] addAssetPaths(String[] paths) {
- if (paths == null) {
- return null;
- }
-
- int[] cookies = new int[paths.length];
- for (int i = 0; i < paths.length; i++) {
- cookies[i] = addAssetPath(paths[i]);
- }
-
- return cookies;
- }
-
/**
* Determine whether the state in this asset manager is up-to-date with
* the files on the filesystem. If false is returned, you need to
* instantiate a new AssetManager class to see the new data.
- * {@hide}
+ * @hide
*/
- public native final boolean isUpToDate();
+ public boolean isUpToDate() {
+ for (ApkAssets apkAssets : getApkAssets()) {
+ if (!apkAssets.isUpToDate()) {
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Get the locales that this asset manager contains data for.
@@ -784,7 +1071,12 @@
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
* and {@code CC} is a two letter country code.
*/
- public native final String[] getLocales();
+ public String[] getLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, false /*excludeSystem*/);
+ }
+ }
/**
* Same as getLocales(), except that locales that are only provided by the system (i.e. those
@@ -794,131 +1086,57 @@
* assets support Cherokee and French, getLocales() would return
* [Cherokee, English, French, German], while getNonSystemLocales() would return
* [Cherokee, French].
- * {@hide}
+ * @hide
*/
- public native final String[] getNonSystemLocales();
-
- /** {@hide} */
- public native final Configuration[] getSizeConfigurations();
+ public String[] getNonSystemLocales() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetLocales(mObject, true /*excludeSystem*/);
+ }
+ }
/**
- * Change the configuation used when retrieving resources. Not for use by
+ * @hide
+ */
+ Configuration[] getSizeConfigurations() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetSizeConfigurations(mObject);
+ }
+ }
+
+ /**
+ * Change the configuration used when retrieving resources. Not for use by
* applications.
- * {@hide}
+ * @hide
*/
- public native final void setConfiguration(int mcc, int mnc, String locale,
- int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
- int screenLayout, int uiMode, int colorMode, int majorVersion);
+ public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
+ int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+ int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+ int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+ keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+ smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+ colorMode, majorVersion);
+ }
+ }
/**
- * Retrieve the resource identifier for the given resource name.
+ * @hide
*/
- /*package*/ native final int getResourceIdentifier(String name,
- String defType,
- String defPackage);
+ public SparseArray<String> getAssignedPackageIdentifiers() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetAssignedPackageIdentifiers(mObject);
+ }
+ }
- /*package*/ native final String getResourceName(int resid);
- /*package*/ native final String getResourcePackageName(int resid);
- /*package*/ native final String getResourceTypeName(int resid);
- /*package*/ native final String getResourceEntryName(int resid);
-
- private native final long openAsset(String fileName, int accessMode);
- private final native ParcelFileDescriptor openAssetFd(String fileName,
- long[] outOffsets) throws IOException;
- private native final long openNonAssetNative(int cookie, String fileName,
- int accessMode);
- private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
- String fileName, long[] outOffsets) throws IOException;
- private native final void destroyAsset(long asset);
- private native final int readAssetChar(long asset);
- private native final int readAsset(long asset, byte[] b, int off, int len);
- private native final long seekAsset(long asset, long offset, int whence);
- private native final long getAssetLength(long asset);
- private native final long getAssetRemainingLength(long asset);
-
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceValue(int ident, short density, TypedValue outValue,
- boolean resolve);
- /** Returns true if the resource was found, filling in mRetStringBlock and
- * mRetData. */
- private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
- boolean resolve);
- /*package*/ static final int STYLE_NUM_ENTRIES = 6;
- /*package*/ static final int STYLE_TYPE = 0;
- /*package*/ static final int STYLE_DATA = 1;
- /*package*/ static final int STYLE_ASSET_COOKIE = 2;
- /*package*/ static final int STYLE_RESOURCE_ID = 3;
-
- /* Offset within typed data array for native changingConfigurations. */
- static final int STYLE_CHANGING_CONFIGURATIONS = 4;
-
- /*package*/ static final int STYLE_DENSITY = 5;
- /*package*/ native static final void applyStyle(long theme,
- int defStyleAttr, int defStyleRes, long xmlParser,
- int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress);
- /*package*/ native static final boolean resolveAttrs(long theme,
- int defStyleAttr, int defStyleRes, int[] inValues,
- int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final boolean retrieveAttributes(
- long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
- /*package*/ native final int getArraySize(int resource);
- /*package*/ native final int retrieveArray(int resource, int[] outValues);
- private native final int getStringBlockCount();
- private native final long getNativeStringBlock(int block);
-
- /**
- * {@hide}
- */
- public native final String getCookieName(int cookie);
-
- /**
- * {@hide}
- */
- public native final SparseArray<String> getAssignedPackageIdentifiers();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetCount();
-
- /**
- * {@hide}
- */
- public native static final String getAssetAllocations();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetManagerCount();
-
- private native final long newTheme();
- private native final void deleteTheme(long theme);
- /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
- /*package*/ native static final void copyTheme(long dest, long source);
- /*package*/ native static final void clearTheme(long theme);
- /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
- TypedValue outValue,
- boolean resolve);
- /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
- /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
-
- private native final long openXmlAssetNative(int cookie, String fileName);
-
- private native final String[] getArrayStringResource(int arrayRes);
- private native final int[] getArrayStringInfo(int arrayRes);
- /*package*/ native final int[] getArrayIntResource(int arrayRes);
- /*package*/ native final int[] getStyleAttributes(int themeRes);
-
- private native final void init(boolean isSystem);
- private native final void destroy();
-
- private final void incRefsLocked(long id) {
+ private void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
- mRefStacks = new HashMap<Long, RuntimeException>();
+ mRefStacks = new HashMap<>();
}
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
@@ -926,16 +1144,117 @@
}
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<String> nativeGetAssignedPackageIdentifiers(
+ long ptr);
+
+ // File native methods.
+ private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
+ throws IOException;
+ private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
+ @NonNull String fileName, long[] outOffsets) throws IOException;
+ private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
+ int accessMode);
+ private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
+ @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
+ private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+
+ // Primitive resource native methods.
+ private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
+ @NonNull TypedValue outValue, boolean resolveReferences);
+ private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
+ @NonNull TypedValue outValue);
+
+ private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
+ @StyleRes int resId);
+ private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
+ @ArrayRes int resId);
+ private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
+ private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
+ @NonNull int[] outValues);
+
+ // Resource name/ID native methods.
+ private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
+ @Nullable String defType, @Nullable String defPackage);
+ private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourcePackageName(long ptr,
+ @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
+ private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
+ private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
+ private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+
+ // Style attribute retrieval native methods.
+ private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
+ long outValuesAddress, long outIndicesAddress);
+ private static native boolean nativeResolveAttrs(long ptr, long themePtr,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+ private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
+ @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+
+ // Theme related native methods
+ private static native long nativeThemeCreate(long ptr);
+ private static native void nativeThemeDestroy(long themePtr);
+ private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
+ boolean force);
+ static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+ static native void nativeThemeClear(long themePtr);
+ private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
+ @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
+ private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+ String prefix);
+ static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+
+ // AssetInputStream related native methods.
+ private static native void nativeAssetDestroy(long assetPtr);
+ private static native int nativeAssetReadChar(long assetPtr);
+ private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
+ private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
+ private static native long nativeAssetGetLength(long assetPtr);
+ private static native long nativeAssetGetRemainingLength(long assetPtr);
+
+ private static native void nativeVerifySystemIdmaps();
+
+ // Global debug native methods.
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetCount();
+
+ /**
+ * @hide
+ */
+ public static native String getAssetAllocations();
+
+ /**
+ * @hide
+ */
+ public static native int getGlobalAssetManagerCount();
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e173653c..8f58891 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -590,7 +590,7 @@
*/
@NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
if (res != null) {
return res;
}
@@ -613,13 +613,13 @@
@NonNull
public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
final ResourcesImpl impl = mResourcesImpl;
- int len = impl.getAssets().getArraySize(id);
+ int len = impl.getAssets().getResourceArraySize(id);
if (len < 0) {
throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = impl.getAssets().retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().getResourceArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -1789,8 +1789,7 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
- array.mData, array.mIndices);
+ mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
array.mXml = parser;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 97cb78b..2a4b278 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -168,7 +168,6 @@
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
- mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -1274,8 +1273,7 @@
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
+ mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
mKey.append(resId, force);
}
@@ -1284,7 +1282,7 @@
void setTo(ThemeImpl other) {
synchronized (mKey) {
synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
+ AssetManager.nativeThemeCopy(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
mKey.setTo(other.getKey());
@@ -1307,12 +1305,10 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
+ mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+ array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
-
return array;
}
}
@@ -1329,7 +1325,7 @@
}
final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
array.mTheme = wrapper;
array.mXml = null;
return array;
@@ -1349,14 +1345,14 @@
@Config int getChangingConfigurations() {
synchronized (mKey) {
final @NativeConfig int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
+ AssetManager.nativeThemeGetChangingConfigurations(mTheme);
return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
}
}
public void dump(int priority, String tag, String prefix) {
synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ mAssets.dumpTheme(mTheme, priority, tag, prefix);
}
}
@@ -1385,13 +1381,13 @@
*/
void rebase() {
synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
+ AssetManager.nativeThemeClear(mTheme);
// Reapply the same styles in the same order.
for (int i = 0; i < mKey.mCount; i++) {
final int resId = mKey.mResId[i];
final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
+ mAssets.applyStyleToTheme(mTheme, resId, force);
}
}
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f33c7516..cbb3c6d 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -61,6 +61,15 @@
return attrs;
}
+ // STYLE_ prefixed constants are offsets within the typed data array.
+ static final int STYLE_NUM_ENTRIES = 6;
+ static final int STYLE_TYPE = 0;
+ static final int STYLE_DATA = 1;
+ static final int STYLE_ASSET_COOKIE = 2;
+ static final int STYLE_RESOURCE_ID = 3;
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+ static final int STYLE_DENSITY = 5;
+
private final Resources mResources;
private DisplayMetrics mMetrics;
private AssetManager mAssets;
@@ -78,7 +87,7 @@
private void resize(int len) {
mLength = len;
- final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ final int dataLen = len * STYLE_NUM_ENTRIES;
final int indicesLen = len + 1;
final VMRuntime runtime = VMRuntime.getRuntime();
if (mDataAddress == 0 || mData.length < dataLen) {
@@ -166,9 +175,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -203,9 +212,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -242,14 +251,13 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_STRING) {
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]).toString();
+ return mXml.getPooledString(data[index + STYLE_DATA]).toString();
}
}
return null;
@@ -274,11 +282,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
@@ -320,14 +328,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA] != 0;
+ return data[index + STYLE_DATA] != 0;
}
final TypedValue v = mValue;
@@ -359,14 +367,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -396,16 +404,16 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FLOAT) {
- return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+ return Float.intBitsToFloat(data[index + STYLE_DATA]);
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
}
final TypedValue v = mValue;
@@ -446,15 +454,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_STRING) {
final TypedValue value = mValue;
if (getValueAt(index, value)) {
@@ -498,7 +506,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -533,7 +541,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -564,15 +572,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -612,15 +620,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -661,15 +668,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -711,15 +717,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -755,16 +760,15 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -795,15 +799,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
+ return data[index + STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index + AssetManager.STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
}
return defValue;
@@ -833,15 +836,14 @@
}
final int attrIndex = index;
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FRACTION) {
- return TypedValue.complexToFraction(
- data[index+AssetManager.STYLE_DATA], base, pbase);
+ return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -874,10 +876,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
- final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
+ final int resid = data[index + STYLE_RESOURCE_ID];
if (resid != 0) {
return resid;
}
@@ -902,10 +904,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
- return data[index + AssetManager.STYLE_DATA];
+ if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+ return data[index + STYLE_DATA];
}
return defValue;
}
@@ -939,7 +941,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -975,7 +977,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -1006,7 +1008,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return mResources.getTextArray(value.resourceId);
}
return null;
@@ -1027,7 +1029,7 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+ return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
}
/**
@@ -1043,8 +1045,8 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
- return mData[index + AssetManager.STYLE_TYPE];
+ index *= STYLE_NUM_ENTRIES;
+ return mData[index + STYLE_TYPE];
}
/**
@@ -1063,9 +1065,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL;
}
@@ -1084,11 +1086,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= AssetManager.STYLE_NUM_ENTRIES;
+ index *= STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
return type != TypedValue.TYPE_NULL
- || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+ || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
}
/**
@@ -1109,7 +1111,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
return value;
}
return null;
@@ -1181,16 +1183,16 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+ final int index = i * STYLE_NUM_ENTRIES;
+ if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
// Null the entry so that we can safely call getZzz().
- data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
+ data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
- final int attr = data[index + AssetManager.STYLE_DATA];
+ final int attr = data[index + STYLE_DATA];
if (attr == 0) {
// Useless data, ignore.
continue;
@@ -1231,45 +1233,44 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * AssetManager.STYLE_NUM_ENTRIES;
- final int type = data[index + AssetManager.STYLE_TYPE];
+ final int index = i * STYLE_NUM_ENTRIES;
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
continue;
}
changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
}
return changingConfig;
}
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
- final int type = data[index+AssetManager.STYLE_TYPE];
+ final int type = data[index + STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
- outValue.data = data[index+AssetManager.STYLE_DATA];
- outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
- outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+ outValue.data = data[index + STYLE_DATA];
+ outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
+ outValue.resourceId = data[index + STYLE_RESOURCE_ID];
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
- outValue.density = data[index+AssetManager.STYLE_DENSITY];
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
+ outValue.density = data[index + STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
private CharSequence loadStringValueAt(int index) {
final int[] data = mData;
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
if (mXml != null) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]);
+ return mXml.getPooledString(data[index + STYLE_DATA]);
}
return null;
}
- return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
+ return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
}
/** @hide */
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index e6b95741..d4ccffb 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.annotation.Nullable;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -33,7 +34,7 @@
*
* {@hide}
*/
-final class XmlBlock {
+final class XmlBlock implements AutoCloseable {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
+ @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -478,13 +480,13 @@
* are doing! The given native object must exist for the entire lifetime
* of this newly creating XmlBlock.
*/
- XmlBlock(AssetManager assets, long xmlBlock) {
+ XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
mAssets = assets;
mNative = xmlBlock;
mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
}
- private final AssetManager mAssets;
+ private @Nullable final AssetManager mAssets;
private final long mNative;
/*package*/ final StringBlock mStrings;
private boolean mOpen = true;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 543acc7..19cc2d0 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,8 +110,8 @@
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
"android_util_EventLog.cpp",
- "android_util_MemoryIntArray.cpp",
"android_util_Log.cpp",
+ "android_util_MemoryIntArray.cpp",
"android_util_PathParser.cpp",
"android_util_Process.cpp",
"android_util_StringBlock.cpp",
@@ -189,6 +189,7 @@
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
"android_app_backup_FullBackup.cpp",
+ "android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
"android_animation_PropertyValuesHolder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index aa9a824..e3b5c8f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -122,6 +122,7 @@
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
+extern int register_android_content_res_ApkAssets(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
@@ -1340,6 +1341,7 @@
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
+ REG_JNI(register_android_content_res_ApkAssets),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_MeasuredParagraph),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f0..c50026e 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -28,7 +28,7 @@
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -224,7 +224,8 @@
NPE_CHECK_RETURN_ZERO(env, jpath);
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
+
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
if (NULL == mgr) {
builder->axes.clear();
return false;
@@ -236,27 +237,33 @@
return false;
}
- Asset* asset;
- if (isAsset) {
- asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- } else {
- asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
- Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ if (isAsset) {
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ } else if (cookie > 0) {
+ // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
+ asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
+ Asset::ACCESS_BUFFER);
+ } else {
+ asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ }
}
- if (NULL == asset) {
+ if (nullptr == asset) {
builder->axes.clear();
return false;
}
const void* buf = asset->getBuffer(false);
if (NULL == buf) {
- delete asset;
builder->axes.clear();
return false;
}
- sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset));
+ sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
+ asset.release()));
return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 09e37e1..49a24a3 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -361,7 +361,7 @@
code->sdkVersion = sdkVersion;
code->javaAssetManager = env->NewGlobalRef(jAssetMgr);
- code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
+ code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr);
if (obbDir != NULL) {
dirStr = env->GetStringUTFChars(obbDir, NULL);
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
new file mode 100644
index 0000000..c0f151b
--- /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<const ApkAssets> apk_assets;
+ if (overlay) {
+ apk_assets = ApkAssets::LoadOverlay(path.c_str(), system);
+ } else if (force_shared_lib) {
+ apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
+ } else {
+ apk_assets = ApkAssets::Load(path.c_str(), system);
+ }
+
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
+ jstring friendly_name, jboolean system, jboolean force_shared_lib) {
+ ScopedUtfChars friendly_name_utf8(env, friendly_name);
+ if (friendly_name_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ unique_fd dup_fd(::dup(fd));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
+ friendly_name_utf8.c_str(),
+ system, force_shared_lib);
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
+ friendly_name_utf8.c_str(), dup_fd.get());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<ApkAssets*>(ptr);
+}
+
+static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return env->NewStringUTF(apk_assets->GetPath().c_str());
+}
+
+static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+}
+
+static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ (void)apk_assets;
+ return JNI_TRUE;
+}
+
+static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
+ ScopedUtfChars path_utf8(env, file_name);
+ if (path_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(),
+ Asset::AccessMode::ACCESS_RANDOM);
+ if (asset == nullptr) {
+ jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
+ return 0;
+ }
+
+ // DynamicRefTable is only needed when looking up resource references. Opening an XML file
+ // directly from an ApkAssets has no notion of proper resource references.
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
+// JNI registration.
+static const JNINativeMethod gApkAssetsMethods[] = {
+ {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad},
+ {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J",
+ (void*)NativeLoadFromFd},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
+ {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
+ {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
+ {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
+};
+
+int register_android_content_res_ApkAssets(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
+ arraysize(gApkAssetsMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c6828c4..403937b 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1,1846 +1,1437 @@
-/* //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 <android_runtime/android_util_AssetManager.h>
-
#include <inttypes.h>
#include <linux/capability.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include "androidfw/Asset.h"
-#include "androidfw/AssetManager.h"
-#include "androidfw/AttributeResolution.h"
-#include "androidfw/ResourceTypes.h"
+#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
+#include "android_runtime/android_util_AssetManager.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_util_Binder.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/MutexGuard.h"
+#include "androidfw/ResourceTypes.h"
#include "core_jni_helpers.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedStringChars.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include "nativehelper/JNIHelp.h"
+#include "nativehelper/ScopedPrimitiveArray.h"
+#include "nativehelper/ScopedStringChars.h"
+#include "nativehelper/ScopedUtfChars.h"
#include "utils/Log.h"
-#include "utils/misc.h"
#include "utils/String8.h"
+#include "utils/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);
+// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
+constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
+ return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
+}
-jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
- const Res_value& value, uint32_t ref, ssize_t block,
- uint32_t typeSpecFlags, ResTable_config* config)
-{
- env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
- env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
- static_cast<jint>(table->getTableCookie(block)));
- env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
- env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
- env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
- env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
- typeSpecFlags);
- if (config != NULL) {
- env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
- }
- return block;
+constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
+ return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
// This is called by zygote (running as user root) as part of preloadResources.
-static void verifySystemIdmaps()
-{
- pid_t pid;
- char system_id[10];
+static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
+ switch (pid_t pid = fork()) {
+ case -1:
+ PLOG(ERROR) << "failed to fork for idmap";
+ break;
- snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+ // child
+ case 0: {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
- switch (pid = fork()) {
- case -1:
- ALOGE("failed to fork for idmap: %s", strerror(errno));
- break;
- case 0: // child
- {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata;
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
+ if (capget(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capget";
+ exit(1);
+ }
- if (capget(&capheader, &capdata) != 0) {
- ALOGE("capget: %s\n", strerror(errno));
- exit(1);
- }
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capset";
+ exit(1);
+ }
- capdata.effective = capdata.permitted;
- if (capset(&capheader, &capdata) != 0) {
- ALOGE("capset: %s\n", strerror(errno));
- exit(1);
- }
+ if (setgid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setgid";
+ exit(1);
+ }
- if (setgid(AID_SYSTEM) != 0) {
- ALOGE("setgid: %s\n", strerror(errno));
- exit(1);
- }
+ if (setuid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setuid";
+ exit(1);
+ }
- if (setuid(AID_SYSTEM) != 0) {
- ALOGE("setuid: %s\n", strerror(errno));
- exit(1);
- }
+ // Generic idmap parameters
+ const char* argv[8];
+ int argc = 0;
+ struct stat st;
- // Generic idmap parameters
- const char* argv[8];
- int argc = 0;
- struct stat st;
+ memset(argv, 0, sizeof(argv));
+ argv[argc++] = AssetManager::IDMAP_BIN;
+ argv[argc++] = "--scan";
+ argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
+ argv[argc++] = AssetManager::TARGET_APK_PATH;
+ argv[argc++] = AssetManager::IDMAP_DIR;
- memset(argv, NULL, sizeof(argv));
- argv[argc++] = AssetManager::IDMAP_BIN;
- argv[argc++] = "--scan";
- argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
- argv[argc++] = AssetManager::TARGET_APK_PATH;
- argv[argc++] = AssetManager::IDMAP_DIR;
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ 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();
+ }
+ }
- // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
- char subdir[PROP_VALUE_MAX];
- int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
- if (len > 0) {
- String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
- if (stat(overlayPath.string(), &st) == 0) {
- argv[argc++] = overlayPath.string();
- }
- }
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
- }
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::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;
- }
+ // 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 jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
+ uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
+ ApkAssetsCookieToJavaCookie(cookie));
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
+ env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
+ if (config != nullptr) {
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
+ }
+ return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
}
// ----------------------------------------------------------------------------
-// this guy is exported to other jni routines
-AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
-{
- jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
- AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
- if (am != NULL) {
- return am;
- }
- jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
- return NULL;
-}
-
-static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
- jstring fileName, jint mode)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
- return -1;
- }
-
- if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
- && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return -1;
- }
-
- Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return -1;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return reinterpret_cast<jlong>(a);
-}
-
-static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
-{
- off64_t startOffset, length;
- int fd = a->openFileDescriptor(&startOffset, &length);
- delete a;
-
- if (fd < 0) {
- jniThrowException(env, "java/io/FileNotFoundException",
- "This file can not be opened as a file descriptor; it is probably compressed");
- return NULL;
- }
-
- jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
- if (offsets == NULL) {
- close(fd);
- return NULL;
- }
-
- offsets[0] = startOffset;
- offsets[1] = length;
-
- env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
-
- jobject fileDesc = jniCreateFileDescriptor(env, fd);
- if (fileDesc == NULL) {
- close(fd);
- return NULL;
- }
-
- return newParcelFileDescriptor(env, fileDesc);
-}
-
-static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
- jstring fileName, jlongArray outOffsets)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return returnParcelFileDescriptor(env, a, outOffsets);
-}
-
-static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName,
- jint mode)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return -1;
- }
-
- if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
- && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return -1;
- }
-
- Asset* a = cookie
- ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(),
- (Asset::AccessMode)mode)
- : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return -1;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return reinterpret_cast<jlong>(a);
-}
-
-static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName,
- jlongArray outOffsets)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- Asset* a = cookie
- ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM)
- : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- //printf("Created Asset Stream: %p\n", a);
-
- return returnParcelFileDescriptor(env, a, outOffsets);
-}
-
-static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
- jstring fileName)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return NULL;
- }
-
- AssetDir* dir = am->openDir(fileName8.c_str());
-
- if (dir == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return NULL;
- }
-
- size_t N = dir->getFileCount();
-
- jobjectArray array = env->NewObjectArray(dir->getFileCount(),
- g_stringClass, NULL);
- if (array == NULL) {
- delete dir;
- return NULL;
- }
-
- for (size_t i=0; i<N; i++) {
- const String8& name = dir->getFileName(i);
- jstring str = env->NewStringUTF(name.string());
- if (str == NULL) {
- delete dir;
- return NULL;
- }
- env->SetObjectArrayElement(array, i, str);
- env->DeleteLocalRef(str);
- }
-
- delete dir;
-
- return array;
-}
-
-static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- //printf("Destroying Asset Stream: %p\n", a);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return;
- }
-
- delete a;
-}
-
-static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- uint8_t b;
- ssize_t res = a->read(&b, 1);
- return res == 1 ? b : -1;
-}
-
-static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle, jbyteArray bArray,
- jint off, jint len)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL || bArray == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- if (len == 0) {
- return 0;
- }
-
- jsize bLen = env->GetArrayLength(bArray);
- if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
- return -1;
- }
-
- jbyte* b = env->GetByteArrayElements(bArray, NULL);
- ssize_t res = a->read(b+off, len);
- env->ReleaseByteArrayElements(bArray, b, 0);
-
- if (res > 0) return static_cast<jint>(res);
-
- if (res < 0) {
- jniThrowException(env, "java/io/IOException", "");
- }
- return -1;
-}
-
-static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle,
- jlong offset, jint whence)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->seek(
- offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
-}
-
-static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->getLength();
-}
-
-static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->getRemainingLength();
-}
-
-static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
- jstring path, jboolean appAsLib)
-{
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
- jstring idmapPath)
-{
- ScopedUtfChars idmapPath8(env, idmapPath);
- if (idmapPath8.c_str() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
-
- return (res) ? (jint)cookie : 0;
-}
-
-static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
- jobject fileDescriptor, jstring debugPathName,
- jboolean appAsLib)
-{
- ScopedUtfChars debugPathName8(env, debugPathName);
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (fd < 0) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int dupfd = ::dup(fd);
- if (dupfd < 0) {
- jniThrowIOException(env, errno);
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_TRUE;
- }
- return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
-}
-
-static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
-{
- Vector<String8> locales;
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- am->getLocales(&locales, includeSystemLocales);
-
- const int N = locales.size();
-
- jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jstring str = env->NewStringUTF(locales[i].string());
- if (str == NULL) {
- return NULL;
- }
- env->SetObjectArrayElement(result, i, str);
- env->DeleteLocalRef(str);
- }
-
- return result;
-}
-
-static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, true /* include system locales */);
-}
-
-static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, false /* don't include system locales */);
-}
-
-static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
- jobject result = env->NewObject(gConfigurationOffsets.classObject,
- gConfigurationOffsets.constructor);
- if (result == NULL) {
- return NULL;
- }
-
- env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
- config.smallestScreenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
-
- return result;
-}
-
-static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
- const Vector<ResTable_config>& configs) {
- const int N = configs.size();
- jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jobject config = constructConfigurationObject(env, configs[i]);
- if (config == NULL) {
- env->DeleteLocalRef(result);
- return NULL;
- }
-
- env->SetObjectArrayElement(result, i, config);
- env->DeleteLocalRef(config);
- }
-
- return result;
-}
-
-static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- const ResTable& res(am->getResources());
- Vector<ResTable_config> configs;
- res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
-
- return getSizeConfigurationsInternal(env, configs);
-}
-
-static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
- jint mcc, jint mnc,
- jstring locale, jint orientation,
- jint touchscreen, jint density,
- jint keyboard, jint keyboardHidden,
- jint navigation,
- jint screenWidth, jint screenHeight,
- jint smallestScreenWidthDp,
- jint screenWidthDp, jint screenHeightDp,
- jint screenLayout, jint uiMode,
- jint colorMode, jint sdkVersion)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return;
- }
-
- ResTable_config config;
- memset(&config, 0, sizeof(config));
-
- const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
-
- // Constants duplicated from Java class android.content.res.Configuration.
- static const jint kScreenLayoutRoundMask = 0x300;
- static const jint kScreenLayoutRoundShift = 8;
-
- config.mcc = (uint16_t)mcc;
- config.mnc = (uint16_t)mnc;
- config.orientation = (uint8_t)orientation;
- config.touchscreen = (uint8_t)touchscreen;
- config.density = (uint16_t)density;
- config.keyboard = (uint8_t)keyboard;
- config.inputFlags = (uint8_t)keyboardHidden;
- config.navigation = (uint8_t)navigation;
- config.screenWidth = (uint16_t)screenWidth;
- config.screenHeight = (uint16_t)screenHeight;
- config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
- config.screenWidthDp = (uint16_t)screenWidthDp;
- config.screenHeightDp = (uint16_t)screenHeightDp;
- config.screenLayout = (uint8_t)screenLayout;
- config.uiMode = (uint8_t)uiMode;
- config.colorMode = (uint8_t)colorMode;
- config.sdkVersion = (uint16_t)sdkVersion;
- config.minorVersion = 0;
-
- // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
- // in C++. We must extract the round qualifier out of the Java screenLayout and put it
- // into screenLayout2.
- config.screenLayout2 =
- (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
-
- am->setConfiguration(config, locale8);
-
- if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
-}
-
-static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
- jstring name,
- jstring defType,
- jstring defPackage)
-{
- ScopedStringChars name16(env, name);
- if (name16.get() == NULL) {
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
- : NULL;
- jsize defTypeLen = defType
- ? env->GetStringLength(defType) : 0;
- const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
- NULL))
- : NULL;
- jsize defPackageLen = defPackage
- ? env->GetStringLength(defPackage) : 0;
-
- jint ident = am->getResources().identifierForName(
- reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
- defType16, defTypeLen, defPackage16, defPackageLen);
-
- if (defPackage16) {
- env->ReleaseStringChars(defPackage,
- reinterpret_cast<const jchar*>(defPackage16));
- }
- if (defType16) {
- env->ReleaseStringChars(defType,
- reinterpret_cast<const jchar*>(defType16));
- }
-
- return ident;
-}
-
-static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- String16 str;
- if (name.package != NULL) {
- str.setTo(name.package, name.packageLen);
- }
- if (name.type8 != NULL || name.type != NULL) {
- if (str.size() > 0) {
- char16_t div = ':';
- str.append(&div, 1);
- }
- if (name.type8 != NULL) {
- str.append(String16(name.type8, name.typeLen));
- } else {
- str.append(name.type, name.typeLen);
- }
- }
- if (name.name8 != NULL || name.name != NULL) {
- if (str.size() > 0) {
- char16_t div = '/';
- str.append(&div, 1);
- }
- if (name.name8 != NULL) {
- str.append(String16(name.name8, name.nameLen));
- } else {
- str.append(name.name, name.nameLen);
- }
- }
-
- return env->NewString((const jchar*)str.string(), str.size());
-}
-
-static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.package != NULL) {
- return env->NewString((const jchar*)name.package, name.packageLen);
- }
-
- return NULL;
-}
-
-static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.type8 != NULL) {
- return env->NewStringUTF(name.type8);
- }
-
- if (name.type != NULL) {
- return env->NewString((const jchar*)name.type, name.typeLen);
- }
-
- return NULL;
-}
-
-static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
- jint resid)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- ResTable::resource_name name;
- if (!am->getResources().getResourceName(resid, true, &name)) {
- return NULL;
- }
-
- if (name.name8 != NULL) {
- return env->NewStringUTF(name.name8);
- }
-
- if (name.name != NULL) {
- return env->NewString((const jchar*)name.name, name.nameLen);
- }
-
- return NULL;
-}
-
-static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
- jint ident,
- jshort density,
- jobject outValue,
- jboolean resolve)
-{
- if (outValue == NULL) {
- jniThrowNullPointerException(env, "outValue");
- return 0;
- }
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- Res_value value;
- ResTable_config config;
- uint32_t typeSpecFlags;
- ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- uint32_t ref = ident;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- if (block >= 0) {
- return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
- }
-
- return static_cast<jint>(block);
-}
-
-static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
- jint ident, jint bagEntryId,
- jobject outValue, jboolean resolve)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- ssize_t block = -1;
- Res_value value;
-
- const ResTable::bag_entry* entry = NULL;
- uint32_t typeSpecFlags;
- ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
-
- for (ssize_t i=0; i<entryCount; i++) {
- if (((uint32_t)bagEntryId) == entry->map.name.ident) {
- block = entry->stringBlock;
- value = entry->map.value;
- }
- entry++;
- }
-
- res.unlock();
-
- if (block < 0) {
- return static_cast<jint>(block);
- }
-
- uint32_t ref = ident;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- if (block >= 0) {
- return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags);
- }
-
- return static_cast<jint>(block);
-}
-
-static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return am->getResources().getTableCount();
-}
-
-static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
- jint block)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
-}
-
-static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
- jint cookie)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- String8 name(am->getAssetPath(static_cast<int32_t>(cookie)));
- if (name.length() == 0) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
- return NULL;
- }
- jstring str = env->NewStringUTF(name.string());
- return str;
-}
-
-static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- const ResTable& res = am->getResources();
-
- jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject,
- gSparseArrayOffsets.constructor);
- const size_t N = res.getBasePackageCount();
- for (size_t i = 0; i < N; i++) {
- const String16 name = res.getBasePackageName(i);
- env->CallVoidMethod(
- sparseArray, gSparseArrayOffsets.put,
- static_cast<jint>(res.getBasePackageId(i)),
- env->NewString(reinterpret_cast<const jchar*>(name.string()),
- name.size()));
- }
- return sparseArray;
-}
-
-static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
-}
-
-static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- delete theme;
-}
-
-static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
- jlong themeHandle,
- jint styleRes,
- jboolean force)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- theme->applyStyle(styleRes, force ? true : false);
-}
-
-static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
- jlong destHandle, jlong srcHandle)
-{
- ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
- ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
- dest->setTo(*src);
-}
-
-static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- theme->clear();
-}
-
-static jint android_content_AssetManager_loadThemeAttributeValue(
- JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- const ResTable& res(theme->getResTable());
-
- Res_value value;
- // XXX value could be different in different configs!
- uint32_t typeSpecFlags = 0;
- ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
- uint32_t ref = 0;
- if (resolve) {
- block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return 0;
- }
- }
- }
- return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
-}
-
-static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
- jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- return theme->getChangingConfigurations();
-}
-
-static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle, jint pri,
- jstring tag, jstring prefix)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- const ResTable& res(theme->getResTable());
- (void)res;
-
- // XXX Need to use params.
- theme->dumpToLog();
-}
-
-static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
- jlong themeToken,
- jint defStyleAttr,
- jint defStyleRes,
- jintArray inValues,
- jintArray attrs,
- jintArray outValues,
- jintArray outIndices)
-{
- if (themeToken == 0) {
- jniThrowNullPointerException(env, "theme token");
- return JNI_FALSE;
- }
- if (attrs == NULL) {
- jniThrowNullPointerException(env, "attrs");
- return JNI_FALSE;
- }
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- const jsize NI = env->GetArrayLength(attrs);
- const jsize NV = env->GetArrayLength(outValues);
- if (NV < (NI*STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
- return JNI_FALSE;
- }
-
- jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
- if (src == NULL) {
- return JNI_FALSE;
- }
-
- jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
- const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- if (baseDest == NULL) {
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return JNI_FALSE;
- }
-
- jint* indices = NULL;
- if (outIndices != NULL) {
- if (env->GetArrayLength(outIndices) > NI) {
- indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
- }
- }
-
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
- (uint32_t*) srcValues, NSV,
- (uint32_t*) src, NI,
- (uint32_t*) baseDest,
- (uint32_t*) indices);
-
- if (indices != NULL) {
- env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
- }
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
- env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken,
- jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length,
- jlong outValuesAddress, jlong outIndicesAddress) {
- jint* attrs = env->GetIntArrayElements(attrsObj, 0);
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
- uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
- uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
- ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
- reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
- env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
-}
-
-static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
- jlong xmlParserToken,
- jintArray attrs,
- jintArray outValues,
- jintArray outIndices)
-{
- if (xmlParserToken == 0) {
- jniThrowNullPointerException(env, "xmlParserToken");
- return JNI_FALSE;
- }
- if (attrs == NULL) {
- jniThrowNullPointerException(env, "attrs");
- return JNI_FALSE;
- }
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_FALSE;
- }
- const ResTable& res(am->getResources());
- ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
-
- const jsize NI = env->GetArrayLength(attrs);
- const jsize NV = env->GetArrayLength(outValues);
- if (NV < (NI*STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
- return JNI_FALSE;
- }
-
- jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
- if (src == NULL) {
- return JNI_FALSE;
- }
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- if (baseDest == NULL) {
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return JNI_FALSE;
- }
-
- jint* indices = NULL;
- if (outIndices != NULL) {
- if (env->GetArrayLength(outIndices) > NI) {
- indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
- }
- }
-
- bool result = RetrieveAttributes(&res, xmlParser,
- (uint32_t*) src, NI,
- (uint32_t*) baseDest,
- (uint32_t*) indices);
-
- if (indices != NULL) {
- env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
- }
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
- jint id)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- const ResTable& res(am->getResources());
-
- res.lock();
- const ResTable::bag_entry* defStyleEnt = NULL;
- ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
- res.unlock();
-
- return static_cast<jint>(bagOff);
-}
-
-static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
- jint id,
- jintArray outValues)
-{
- if (outValues == NULL) {
- jniThrowNullPointerException(env, "out values");
- return JNI_FALSE;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return JNI_FALSE;
- }
- const ResTable& res(am->getResources());
- ResTable_config config;
- Res_value value;
- ssize_t block;
-
- const jsize NV = env->GetArrayLength(outValues);
-
- jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- jint* dest = baseDest;
- if (dest == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
- return JNI_FALSE;
- }
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- const ResTable::bag_entry* arrayEnt = NULL;
- uint32_t arrayTypeSetFlags = 0;
- ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
- const ResTable::bag_entry* endArrayEnt = arrayEnt +
- (bagOff >= 0 ? bagOff : 0);
-
- int i = 0;
- uint32_t typeSetFlags;
- while (i < NV && arrayEnt < endArrayEnt) {
- block = arrayEnt->stringBlock;
- typeSetFlags = arrayTypeSetFlags;
- config.density = 0;
- value = arrayEnt->map.value;
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- //printf("Resolving attribute reference\n");
- ssize_t newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (kThrowOnBadId) {
- if (newBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return JNI_FALSE;
- }
- }
- if (newBlock >= 0) block = newBlock;
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- }
-
- //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
-
- // Write the final value back to Java.
- dest[STYLE_TYPE] = value.dataType;
- dest[STYLE_DATA] = value.data;
- dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
- dest[STYLE_RESOURCE_ID] = resid;
- dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- dest[STYLE_DENSITY] = config.density;
- dest += STYLE_NUM_ENTRIES;
- i+= STYLE_NUM_ENTRIES;
- arrayEnt++;
- }
-
- i /= STYLE_NUM_ENTRIES;
-
- res.unlock();
-
- env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
-
- return i;
-}
-
-static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
- jint cookie,
- jstring fileName)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
-
- ScopedUtfChars fileName8(env, fileName);
- if (fileName8.c_str() == NULL) {
- return 0;
- }
-
- int32_t assetCookie = static_cast<int32_t>(cookie);
- Asset* a = assetCookie
- ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
- : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
-
- if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
- return 0;
- }
-
- const DynamicRefTable* dynamicRefTable =
- am->getResources().getDynamicRefTableForCookie(assetCookie);
- ResXMLTree* block = new ResXMLTree(dynamicRefTable);
- status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
- a->close();
- delete a;
-
- if (err != NO_ERROR) {
- jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
- return 0;
- }
-
- return reinterpret_cast<jlong>(block);
-}
-
-static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N * 2);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
- jint stringIndex = -1;
- jint stringBlock = 0;
- value = bag->map.value;
-
- // Take care of resolving the found resource to its final value.
- stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
- if (value.dataType == Res_value::TYPE_STRING) {
- stringIndex = value.data;
- }
-
- if (kThrowOnBadId) {
- if (stringBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
-
- //todo: It might be faster to allocate a C array to contain
- // the blocknums and indices, put them in there and then
- // do just one SetIntArrayRegion()
- env->SetIntArrayRegion(array, j, 1, &stringBlock);
- env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
- j = j + 2;
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
- if (env->ExceptionCheck()) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- size_t strLen = 0;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- value = bag->map.value;
- jstring str = NULL;
-
- // Take care of resolving the found resource to its final value.
- ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
- if (value.dataType == Res_value::TYPE_STRING) {
- const ResStringPool* pool = res.getTableStringBlock(block);
- const char* str8 = pool->string8At(value.data, &strLen);
- if (str8 != NULL) {
- str = env->NewStringUTF(str8);
- } else {
- const char16_t* str16 = pool->stringAt(value.data, &strLen);
- str = env->NewString(reinterpret_cast<const jchar*>(str16),
- strLen);
- }
-
- // If one of our NewString{UTF} calls failed due to memory, an
- // exception will be pending.
- if (env->ExceptionCheck()) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- env->SetObjectArrayElement(array, i, str);
-
- // str is not NULL at that point, otherwise ExceptionCheck would have been true.
- // If we have a large amount of strings in our array, we might
- // overflow the local reference table of the VM.
- env->DeleteLocalRef(str);
- }
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
- jint arrayResId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(arrayResId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- Res_value value;
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- value = bag->map.value;
-
- // Take care of resolving the found resource to its final value.
- ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
- if (kThrowOnBadId) {
- if (block == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return array;
- }
- }
- if (value.dataType >= Res_value::TYPE_FIRST_INT
- && value.dataType <= Res_value::TYPE_LAST_INT) {
- int intVal = value.data;
- env->SetIntArrayRegion(array, i, 1, &intVal);
- }
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
- jint styleId)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
- const ResTable& res(am->getResources());
-
- const ResTable::bag_entry* startOfBag;
- const ssize_t N = res.lockBag(styleId, &startOfBag);
- if (N < 0) {
- return NULL;
- }
-
- jintArray array = env->NewIntArray(N);
- if (array == NULL) {
- res.unlockBag(startOfBag);
- return NULL;
- }
-
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- int resourceId = bag->map.name.ident;
- env->SetIntArrayRegion(array, i, 1, &resourceId);
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
-{
- if (isSystem) {
- verifySystemIdmaps();
- }
- AssetManager* am = new AssetManager();
- if (am == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
- return;
- }
-
- am->addDefaultAssets();
-
- ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
- env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
-}
-
-static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = (AssetManager*)
- (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
- ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
- if (am != NULL) {
- delete am;
- env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
- }
-}
-
-static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
-{
- return Asset::getGlobalCount();
-}
-
-static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
-{
- String8 alloc = Asset::getAssetAllocations();
- if (alloc.length() <= 0) {
- return NULL;
- }
-
- jstring str = env->NewStringUTF(alloc.string());
- return str;
-}
-
-static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
-{
- return AssetManager::getGlobalCount();
-}
-
-// ----------------------------------------------------------------------------
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gAssetManagerMethods[] = {
- /* name, signature, funcPtr */
-
- // Basic asset stuff.
- { "openAsset", "(Ljava/lang/String;I)J",
- (void*) android_content_AssetManager_openAsset },
- { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*) android_content_AssetManager_openAssetFd },
- { "openNonAssetNative", "(ILjava/lang/String;I)J",
- (void*) android_content_AssetManager_openNonAssetNative },
- { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*) android_content_AssetManager_openNonAssetFdNative },
- { "list", "(Ljava/lang/String;)[Ljava/lang/String;",
- (void*) android_content_AssetManager_list },
- { "destroyAsset", "(J)V",
- (void*) android_content_AssetManager_destroyAsset },
- { "readAssetChar", "(J)I",
- (void*) android_content_AssetManager_readAssetChar },
- { "readAsset", "(J[BII)I",
- (void*) android_content_AssetManager_readAsset },
- { "seekAsset", "(JJI)J",
- (void*) android_content_AssetManager_seekAsset },
- { "getAssetLength", "(J)J",
- (void*) android_content_AssetManager_getAssetLength },
- { "getAssetRemainingLength", "(J)J",
- (void*) android_content_AssetManager_getAssetRemainingLength },
- { "addAssetPathNative", "(Ljava/lang/String;Z)I",
- (void*) android_content_AssetManager_addAssetPath },
- { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I",
- (void*) android_content_AssetManager_addAssetFd },
- { "addOverlayPathNative", "(Ljava/lang/String;)I",
- (void*) android_content_AssetManager_addOverlayPath },
- { "isUpToDate", "()Z",
- (void*) android_content_AssetManager_isUpToDate },
-
- // Resources.
- { "getLocales", "()[Ljava/lang/String;",
- (void*) android_content_AssetManager_getLocales },
- { "getNonSystemLocales", "()[Ljava/lang/String;",
- (void*) android_content_AssetManager_getNonSystemLocales },
- { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
- (void*) android_content_AssetManager_getSizeConfigurations },
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
- (void*) android_content_AssetManager_setConfiguration },
- { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*) android_content_AssetManager_getResourceIdentifier },
- { "getResourceName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceName },
- { "getResourcePackageName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourcePackageName },
- { "getResourceTypeName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceTypeName },
- { "getResourceEntryName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getResourceEntryName },
- { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadResourceValue },
- { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadResourceBagValue },
- { "getStringBlockCount","()I",
- (void*) android_content_AssetManager_getStringBlockCount },
- { "getNativeStringBlock","(I)J",
- (void*) android_content_AssetManager_getNativeStringBlock },
- { "getCookieName","(I)Ljava/lang/String;",
- (void*) android_content_AssetManager_getCookieName },
- { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
- (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
-
- // Themes.
- { "newTheme", "()J",
- (void*) android_content_AssetManager_newTheme },
- { "deleteTheme", "(J)V",
- (void*) android_content_AssetManager_deleteTheme },
- { "applyThemeStyle", "(JIZ)V",
- (void*) android_content_AssetManager_applyThemeStyle },
- { "copyTheme", "(JJ)V",
- (void*) android_content_AssetManager_copyTheme },
- { "clearTheme", "(J)V",
- (void*) android_content_AssetManager_clearTheme },
- { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
- (void*) android_content_AssetManager_loadThemeAttributeValue },
- { "getThemeChangingConfigurations", "(J)I",
- (void*) android_content_AssetManager_getThemeChangingConfigurations },
- { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
- (void*) android_content_AssetManager_dumpTheme },
- { "applyStyle","(JIIJ[IIJJ)V",
- (void*) android_content_AssetManager_applyStyle },
- { "resolveAttrs","(JII[I[I[I[I)Z",
- (void*) android_content_AssetManager_resolveAttrs },
- { "retrieveAttributes","(J[I[I[I)Z",
- (void*) android_content_AssetManager_retrieveAttributes },
- { "getArraySize","(I)I",
- (void*) android_content_AssetManager_getArraySize },
- { "retrieveArray","(I[I)I",
- (void*) android_content_AssetManager_retrieveArray },
-
- // XML files.
- { "openXmlAssetNative", "(ILjava/lang/String;)J",
- (void*) android_content_AssetManager_openXmlAssetNative },
-
- // Arrays.
- { "getArrayStringResource","(I)[Ljava/lang/String;",
- (void*) android_content_AssetManager_getArrayStringResource },
- { "getArrayStringInfo","(I)[I",
- (void*) android_content_AssetManager_getArrayStringInfo },
- { "getArrayIntResource","(I)[I",
- (void*) android_content_AssetManager_getArrayIntResource },
- { "getStyleAttributes","(I)[I",
- (void*) android_content_AssetManager_getStyleAttributes },
-
- // Bookkeeping.
- { "init", "(Z)V",
- (void*) android_content_AssetManager_init },
- { "destroy", "()V",
- (void*) android_content_AssetManager_destroy },
- { "getGlobalAssetCount", "()I",
- (void*) android_content_AssetManager_getGlobalAssetCount },
- { "getAssetAllocations", "()Ljava/lang/String;",
- (void*) android_content_AssetManager_getAssetAllocations },
- { "getGlobalAssetManagerCount", "()I",
- (void*) android_content_AssetManager_getGlobalAssetManagerCount },
+// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
+struct GuardedAssetManager : public ::AAssetManager {
+ Guarded<AssetManager2> guarded_assetmanager;
};
-int register_android_content_AssetManager(JNIEnv* env)
-{
- jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
- gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
- gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
- gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
- "Ljava/lang/CharSequence;");
- gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
- gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
- gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
- "changingConfigurations", "I");
- gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
+::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
+ jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
+ ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
+ if (am == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
+ return nullptr;
+ }
+ return am;
+}
- jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
- gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
- "Landroid/os/ParcelFileDescriptor;");
- gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
- gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
+ if (assetmanager == nullptr) {
+ return nullptr;
+ }
+ return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
+}
- jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
- gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
+ return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
+}
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
+ return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
+}
- jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
- gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
- gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
- "<init>", "()V");
- gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
- "(ILjava/lang/Object;)V");
+static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
+ jlongArray out_offsets) {
+ off64_t start_offset, length;
+ int fd = asset->openFileDescriptor(&start_offset, &length);
+ asset.reset();
- jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
- gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
- gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
- "<init>", "()V");
- gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "smallestScreenWidthDp", "I");
- gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenWidthDp", "I");
- gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenHeightDp", "I");
+ if (fd < 0) {
+ jniThrowException(env, "java/io/FileNotFoundException",
+ "This file can not be opened as a file descriptor; it is probably "
+ "compressed");
+ return nullptr;
+ }
- return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
- NELEM(gAssetManagerMethods));
+ jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
+ if (offsets == nullptr) {
+ close(fd);
+ return nullptr;
+ }
+
+ offsets[0] = start_offset;
+ offsets[1] = length;
+
+ env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
+
+ jobject file_desc = jniCreateFileDescriptor(env, fd);
+ if (file_desc == nullptr) {
+ close(fd);
+ return nullptr;
+ }
+ return newParcelFileDescriptor(env, file_desc);
+}
+
+static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ return Asset::getGlobalCount();
+}
+
+static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
+ String8 alloc = Asset::getAssetAllocations();
+ if (alloc.length() <= 0) {
+ return nullptr;
+ }
+ return env->NewStringUTF(alloc.string());
+}
+
+static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ // TODO(adamlesinski): Switch to AssetManager2.
+ return AssetManager::getGlobalCount();
+}
+
+static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
+ // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
+ // AssetManager2 in a contiguous block (GuardedAssetManager).
+ return reinterpret_cast<jlong>(new GuardedAssetManager());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<GuardedAssetManager*>(ptr);
+}
+
+static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jobjectArray apk_assets_array, jboolean invalidate_caches) {
+ const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
+ std::vector<const ApkAssets*> apk_assets;
+ apk_assets.reserve(apk_assets_len);
+ for (jsize i = 0; i < apk_assets_len; i++) {
+ jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
+ if (obj == nullptr) {
+ std::string msg = StringPrintf("ApkAssets at index %d is null", i);
+ jniThrowNullPointerException(env, msg.c_str());
+ return;
+ }
+
+ jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+}
+
+static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
+ jstring locale, jint orientation, jint touchscreen, jint density,
+ jint keyboard, jint keyboard_hidden, jint navigation,
+ jint screen_width, jint screen_height,
+ jint smallest_screen_width_dp, jint screen_width_dp,
+ jint screen_height_dp, jint screen_layout, jint ui_mode,
+ jint color_mode, jint major_version) {
+ ResTable_config configuration;
+ memset(&configuration, 0, sizeof(configuration));
+ configuration.mcc = static_cast<uint16_t>(mcc);
+ configuration.mnc = static_cast<uint16_t>(mnc);
+ configuration.orientation = static_cast<uint8_t>(orientation);
+ configuration.touchscreen = static_cast<uint8_t>(touchscreen);
+ configuration.density = static_cast<uint16_t>(density);
+ configuration.keyboard = static_cast<uint8_t>(keyboard);
+ configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
+ configuration.navigation = static_cast<uint8_t>(navigation);
+ configuration.screenWidth = static_cast<uint16_t>(screen_width);
+ configuration.screenHeight = static_cast<uint16_t>(screen_height);
+ configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
+ configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
+ configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
+ configuration.screenLayout = static_cast<uint8_t>(screen_layout);
+ configuration.uiMode = static_cast<uint8_t>(ui_mode);
+ configuration.colorMode = static_cast<uint8_t>(color_mode);
+ configuration.sdkVersion = static_cast<uint16_t>(major_version);
+
+ if (locale != nullptr) {
+ ScopedUtfChars locale_utf8(env, locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ configuration.setBcp47Locale(locale_utf8.c_str());
+ }
+
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
+ // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
+ // in C++. We must extract the round qualifier out of the Java screenLayout and put it
+ // into screenLayout2.
+ configuration.screenLayout2 =
+ static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetConfiguration(configuration);
+}
+
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+
+ jobject sparse_array =
+ env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
+
+ if (sparse_array == nullptr) {
+ // An exception is pending.
+ return nullptr;
+ }
+
+ assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
+ jstring jpackage_name = env->NewStringUTF(package_name.c_str());
+ if (jpackage_name == nullptr) {
+ // An exception is pending.
+ return;
+ }
+
+ env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
+ jpackage_name);
+ });
+ return sparse_array;
+}
+
+static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
+ ScopedUtfChars path_utf8(env, path);
+ if (path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ std::vector<std::string> 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<AssetManager2> 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());
+ }
+ });
+ }
+ }
+
+ jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jsize index = 0;
+ for (const std::string& file_path : all_file_paths) {
+ jstring java_string = env->NewStringUTF(file_path.c_str());
+
+ // Check for errors creating the strings (if malformed or no memory).
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, index++, java_string);
+
+ // If we have a large amount of string in our array, we might overflow the
+ // local reference table of the VM.
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
+static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
+ jint access_mode) {
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
+ access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return 0;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset =
+ assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
+ jlongArray out_offsets) {
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return nullptr;
+ }
+ return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
+}
+
+static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path, jint access_mode) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
+ access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return 0;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
+ static_cast<Asset::AccessMode>(access_mode));
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
+ static_cast<Asset::AccessMode>(access_mode));
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path, jlongArray out_offsets) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return nullptr;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return nullptr;
+ }
+ return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
+}
+
+static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
+ jstring asset_path) {
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+ ScopedUtfChars asset_path_utf8(env, asset_path);
+ if (asset_path_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+
+ // May be nullptr.
+ const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
+
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
+static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jshort density, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ ApkAssetsCookie cookie =
+ assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
+ static_cast<uint16_t>(density), &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t ref = static_cast<uint32_t>(resid);
+ if (resolve_references) {
+ cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ }
+ return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
+}
+
+static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jint bag_entry_id, jobject typed_value) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t type_spec_flags = bag->type_spec_flags;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ const Res_value* bag_value = nullptr;
+ for (const ResolvedBag::Entry& entry : bag) {
+ if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
+ cookie = entry.cookie;
+ bag_value = &entry.value;
+
+ // Keep searching (the old implementation did that).
+ }
+ }
+
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ Res_value value = *bag_value;
+ uint32_t ref = static_cast<uint32_t>(resid);
+ ResTable_config selected_config;
+ cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
+}
+
+static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ for (uint32_t i = 0; i < bag->entry_count; i++) {
+ jint attr_resid = bag->entries[i].key;
+ env->SetIntArrayRegion(array, i, 1, &attr_resid);
+ }
+ return array;
+}
+
+static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ for (uint32_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+
+ // Resolve any references to their final value.
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return nullptr;
+ }
+
+ if (value.dataType == Res_value::TYPE_STRING) {
+ const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
+ const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
+
+ jstring java_string = nullptr;
+ size_t str_len;
+ const char* str_utf8 = pool->string8At(value.data, &str_len);
+ if (str_utf8 != nullptr) {
+ java_string = env->NewStringUTF(str_utf8);
+ } else {
+ const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
+ java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
+ }
+
+ // Check for errors creating the strings (if malformed or no memory).
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, i, java_string);
+
+ // If we have a large amount of string in our array, we might overflow the
+ // local reference table of the VM.
+ env->DeleteLocalRef(java_string);
+ }
+ }
+ return array;
+}
+
+static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count * 2);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ return nullptr;
+ }
+
+ jint string_index = -1;
+ if (value.dataType == Res_value::TYPE_STRING) {
+ string_index = static_cast<jint>(value.data);
+ }
+
+ buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
+ buffer[(i * 2) + 1] = string_index;
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ return nullptr;
+ }
+
+ if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
+ buffer[i] = static_cast<jint>(value.data);
+ }
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+ return static_cast<jint>(bag->entry_count);
+}
+
+static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jintArray out_data) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+
+ const jsize out_data_length = env->GetArrayLength(out_data);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
+ return -1;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
+ if (buffer == nullptr) {
+ return -1;
+ }
+
+ jint* cursor = buffer;
+ for (size_t i = 0; i < bag->entry_count; i++) {
+ const ResolvedBag::Entry& entry = bag->entries[i];
+ Res_value value = entry.value;
+ ResTable_config selected_config;
+ selected_config.density = 0;
+ uint32_t flags = bag->type_spec_flags;
+ uint32_t ref;
+ ApkAssetsCookie cookie =
+ assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
+ return -1;
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ }
+
+ cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
+ cursor[STYLE_DATA] = static_cast<jint>(value.data);
+ cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
+ cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
+ cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
+ cursor += STYLE_NUM_ENTRIES;
+ }
+ env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
+ return static_cast<jint>(bag->entry_count);
+}
+
+static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
+ jstring def_type, jstring def_package) {
+ ScopedUtfChars name_utf8(env, name);
+ if (name_utf8.c_str() == nullptr) {
+ // This will throw NPE.
+ return 0;
+ }
+
+ std::string type;
+ if (def_type != nullptr) {
+ ScopedUtfChars type_utf8(env, def_type);
+ CHECK(type_utf8.c_str() != nullptr);
+ type = type_utf8.c_str();
+ }
+
+ std::string package;
+ if (def_package != nullptr) {
+ ScopedUtfChars package_utf8(env, def_package);
+ CHECK(package_utf8.c_str() != nullptr);
+ package = package_utf8.c_str();
+ }
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
+}
+
+static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ std::string result;
+ if (name.package != nullptr) {
+ result.append(name.package, name.package_len);
+ }
+
+ if (name.type != nullptr || name.type16 != nullptr) {
+ if (!result.empty()) {
+ result += ":";
+ }
+
+ if (name.type != nullptr) {
+ result.append(name.type, name.type_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+ }
+ }
+
+ if (name.entry != nullptr || name.entry16 != nullptr) {
+ if (!result.empty()) {
+ result += "/";
+ }
+
+ if (name.entry != nullptr) {
+ result.append(name.entry, name.entry_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+ }
+ }
+ return env->NewStringUTF(result.c_str());
+}
+
+static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.package != nullptr) {
+ return env->NewStringUTF(name.package);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.type != nullptr) {
+ return env->NewStringUTF(name.type);
+ } else if (name.type16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.entry != nullptr) {
+ return env->NewStringUTF(name.entry);
+ } else if (name.entry16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
+ }
+ return nullptr;
+}
+
+static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
+ jboolean exclude_system) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<std::string> locales =
+ assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
+
+ jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ size_t idx = 0;
+ for (const std::string& locale : locales) {
+ jstring java_string = env->NewStringUTF(locale.c_str());
+ if (java_string == nullptr) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(array, idx++, java_string);
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
+static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
+ jobject result =
+ env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
+ if (result == nullptr) {
+ return nullptr;
+ }
+
+ env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
+ config.smallestScreenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
+ return result;
+}
+
+static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<ResTable_config> configurations =
+ assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
+
+ jobjectArray array =
+ env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ size_t idx = 0;
+ for (const ResTable_config& configuration : configurations) {
+ jobject java_configuration = ConstructConfigurationObject(env, configuration);
+ if (java_configuration == nullptr) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, idx++, java_configuration);
+ env->DeleteLocalRef(java_configuration);
+ }
+ return array;
+}
+
+static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
+ jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+ uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
+ uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
+
+ jsize attrs_len = env->GetArrayLength(java_attrs);
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return;
+ }
+
+ ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+ static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ out_values, out_indices);
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+}
+
+static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint def_style_attr, jint def_style_resid, jintArray java_values,
+ jintArray java_attrs, jintArray out_java_values,
+ jintArray out_java_indices) {
+ const jsize attrs_len = env->GetArrayLength(java_attrs);
+ const jsize out_values_len = env->GetArrayLength(out_java_values);
+ if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
+ return JNI_FALSE;
+ }
+
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return JNI_FALSE;
+ }
+
+ jint* values = nullptr;
+ jsize values_len = 0;
+ if (java_values != nullptr) {
+ values_len = env->GetArrayLength(java_values);
+ values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
+ if (values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
+ if (out_values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ return JNI_FALSE;
+ }
+
+ jint* out_indices = nullptr;
+ if (out_java_indices != nullptr) {
+ jsize out_indices_len = env->GetArrayLength(out_java_indices);
+ if (out_indices_len > attrs_len) {
+ out_indices =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
+ if (out_indices == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ bool result = ResolveAttrs(
+ theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
+ reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
+ attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
+ if (out_indices != nullptr) {
+ env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
+ }
+
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
+ if (values != nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ }
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jlong xml_parser_ptr, jintArray java_attrs,
+ jintArray out_java_values, jintArray out_java_indices) {
+ const jsize attrs_len = env->GetArrayLength(java_attrs);
+ const jsize out_values_len = env->GetArrayLength(out_java_values);
+ if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
+ return JNI_FALSE;
+ }
+
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return JNI_FALSE;
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
+ if (out_values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return JNI_FALSE;
+ }
+
+ jint* out_indices = nullptr;
+ if (out_java_indices != nullptr) {
+ jsize out_indices_len = env->GetArrayLength(out_java_indices);
+ if (out_indices_len > attrs_len) {
+ out_indices =
+ reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
+ if (out_indices == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+
+ bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
+ reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ reinterpret_cast<uint32_t*>(out_values),
+ reinterpret_cast<uint32_t*>(out_indices));
+
+ if (out_indices != nullptr) {
+ env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
+ }
+ env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
+}
+
+static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ delete reinterpret_cast<Theme*>(theme_ptr);
+}
+
+static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint resid, jboolean force) {
+ // AssetManager is accessed via the theme, so grab an explicit lock here.
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+
+ // TODO(adamlesinski): Consider surfacing exception when result is failure.
+ // CTS currently expects no exceptions from this method.
+ // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
+ // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
+}
+
+static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
+ jlong src_theme_ptr) {
+ Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
+ Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
+ if (!dst_theme->SetTo(*src_theme)) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Themes are from different AssetManagers");
+ }
+}
+
+static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ reinterpret_cast<Theme*>(theme_ptr)->Clear();
+}
+
+static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint resid, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t ref = 0u;
+ if (resolve_references) {
+ ResTable_config selected_config;
+ cookie =
+ theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ }
+ return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
+}
+
+static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint priority, jstring tag, jstring prefix) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ (void) theme;
+ (void) priority;
+ (void) tag;
+ (void) prefix;
+}
+
+static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
+ jlong theme_ptr) {
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ return static_cast<jint>(theme->GetChangingConfigurations());
+}
+
+static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ delete reinterpret_cast<Asset*>(asset_ptr);
+}
+
+static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ uint8_t b;
+ ssize_t res = asset->read(&b, sizeof(b));
+ return res == sizeof(b) ? static_cast<jint>(b) : -1;
+}
+
+static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
+ jint offset, jint len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ jsize buffer_len = env->GetArrayLength(java_buffer);
+ if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
+ offset > buffer_len - len) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
+ return -1;
+ }
+
+ ScopedByteArrayRW byte_array(env, java_buffer);
+ if (byte_array.get() == nullptr) {
+ return -1;
+ }
+
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ ssize_t res = asset->read(byte_array.get() + offset, len);
+ if (res < 0) {
+ jniThrowException(env, "java/io/IOException", "");
+ return -1;
+ }
+ return res > 0 ? static_cast<jint>(res) : -1;
+}
+
+static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
+ jint whence) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->seek(
+ static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
+}
+
+static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getLength());
+}
+
+static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getRemainingLength());
+}
+
+// ----------------------------------------------------------------------------
+
+// JNI registration.
+static const JNINativeMethod gAssetManagerMethods[] = {
+ // AssetManager setup methods.
+ {"nativeCreate", "()J", (void*)NativeCreate},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
+ (void*)NativeSetConfiguration},
+ {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
+ (void*)NativeGetAssignedPackageIdentifiers},
+
+ // AssetManager file methods.
+ {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
+ {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
+ {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenAssetFd},
+ {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
+ {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenNonAssetFd},
+ {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+
+ // AssetManager resource methods.
+ {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
+ {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
+ (void*)NativeGetResourceBagValue},
+ {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
+ {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
+ (void*)NativeGetResourceStringArray},
+ {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
+ {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
+ {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
+ {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+
+ // AssetManager resource name/ID methods.
+ {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)NativeGetResourceIdentifier},
+ {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
+ {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
+ {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
+ {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+ {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
+ {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeConfigurations},
+
+ // Style attribute related methods.
+ {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
+ {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
+ {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
+
+ // Theme related methods.
+ {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
+ {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
+ {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
+ {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
+ {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
+ {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
+ (void*)NativeThemeGetAttributeValue},
+ {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
+ {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
+
+ // AssetInputStream methods.
+ {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
+ {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
+ {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
+ {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
+ {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
+ {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
+
+ // System/idmap related methods.
+ {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
+
+ // Global management/debug methods.
+ {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
+ {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
+ {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
+};
+
+int register_android_content_AssetManager(JNIEnv* env) {
+ jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
+ gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
+
+ jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
+ gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
+ gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
+ gTypedValueOffsets.mString =
+ GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
+ gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
+ gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
+ gTypedValueOffsets.mChangingConfigurations =
+ GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
+ gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
+
+ jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
+ gAssetFileDescriptorOffsets.mFd =
+ GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
+ gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
+ gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+
+ jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
+ gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+
+ jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
+ gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
+ gSparseArrayOffsets.constructor =
+ GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
+ gSparseArrayOffsets.put =
+ GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
+
+ jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
+ gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
+ gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
+ gConfigurationOffsets.mSmallestScreenWidthDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
+ gConfigurationOffsets.mScreenWidthDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
+ gConfigurationOffsets.mScreenHeightDpOffset =
+ GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
+
+ return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
+ NELEM(gAssetManagerMethods));
}
}; // namespace android
diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h
index 8dd9337..2c1e357 100644
--- a/core/jni/include/android_runtime/android_util_AssetManager.h
+++ b/core/jni/include/android_runtime/android_util_AssetManager.h
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-#ifndef android_util_AssetManager_H
-#define android_util_AssetManager_H
+#ifndef ANDROID_RUNTIME_ASSETMANAGER_H
+#define ANDROID_RUNTIME_ASSETMANAGER_H
-#include <androidfw/AssetManager.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/MutexGuard.h"
#include "jni.h"
namespace android {
-extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr);
+extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
+extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
+extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager);
-}
+} // namespace android
-#endif
+#endif // ANDROID_RUNTIME_ASSETMANAGER_H
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 415d3e3..2fc8e95 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -265,8 +265,6 @@
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 @@
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 60e3845..f912af4 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -20,13 +20,18 @@
#include <log/log.h>
+#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeFinder.h"
-#include "androidfw/ResourceTypes.h"
constexpr bool kDebugStyles = false;
namespace android {
+// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
+static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
+ return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
+}
+
class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
@@ -44,58 +49,53 @@
};
class BagAttributeFinder
- : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
public:
- BagAttributeFinder(const ResTable::bag_entry* start,
- const ResTable::bag_entry* end)
- : BackTrackingAttributeFinder(start, end) {}
+ BagAttributeFinder(const ResolvedBag* bag)
+ : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
+ bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
+ }
- inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
- return entry->map.name.ident;
+ inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const {
+ return entry->key;
}
};
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
- uint32_t def_style_res, uint32_t* src_values,
- size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values,
- uint32_t* out_indices) {
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+ uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
def_style_attr, def_style_res);
}
- const ResTable& res = theme->getResTable();
+ AssetManager2* assetmanager = theme->GetAssetManager();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_bag_type_set_flags = 0;
+ uint32_t def_style_flags = 0u;
if (def_style_attr != 0) {
Res_value value;
- if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
+ if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = value.data;
}
}
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
// Retrieve the default style bag, if requested.
- const ResTable::bag_entry* def_style_start = nullptr;
- uint32_t def_style_type_set_flags = 0;
- ssize_t bag_off = def_style_res != 0
- ? res.getBagLocked(def_style_res, &def_style_start,
- &def_style_type_set_flags)
- : -1;
- def_style_type_set_flags |= def_style_bag_type_set_flags;
- const ResTable::bag_entry* const def_style_end =
- def_style_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
+ const ResolvedBag* default_style_bag = nullptr;
+ if (def_style_res != 0) {
+ default_style_bag = assetmanager->GetBag(def_style_res);
+ if (default_style_bag != nullptr) {
+ def_style_flags |= default_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -106,7 +106,7 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ssize_t block = -1;
+ ApkAssetsCookie cookie = kInvalidCookie;
uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
@@ -122,15 +122,14 @@
value.dataType = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else {
- const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
- if (def_style_entry != def_style_end) {
- block = def_style_entry->stringBlock;
- type_set_flags = def_style_type_set_flags;
- value = def_style_entry->map.value;
+ const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
+ if (entry != def_style_attr_finder.end()) {
+ cookie = entry->cookie;
+ type_set_flags = def_style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -140,22 +139,26 @@
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block =
- theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ ApkAssetsCookie new_cookie =
+ theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
if (kDebugStyles) {
ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
- if (new_block >= 0) {
+ // If we still don't have a value for this attribute, try to find it in the theme!
+ ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+ if (new_cookie != kInvalidCookie) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ new_cookie =
+ assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
if (kDebugStyles) {
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -169,7 +172,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = -1;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -179,9 +182,7 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -195,90 +196,80 @@
out_values += STYLE_NUM_ENTRIES;
}
- res.unlock();
-
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
return true;
}
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
- theme, def_style_attr, def_style_res, xml_parser);
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+ def_style_attr, def_style_resid, xml_parser);
}
- const ResTable& res = theme->getResTable();
+ AssetManager2* assetmanager = theme->GetAssetManager();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_bag_type_set_flags = 0;
+ uint32_t def_style_flags = 0u;
if (def_style_attr != 0) {
Res_value value;
- if (theme->getAttribute(def_style_attr, &value,
- &def_style_bag_type_set_flags) >= 0) {
+ if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_res = value.data;
+ def_style_resid = value.data;
}
}
}
- // Retrieve the style class associated with the current XML tag.
- int style = 0;
- uint32_t style_bag_type_set_flags = 0;
+ // Retrieve the style resource ID associated with the current XML tag's style attribute.
+ uint32_t style_resid = 0u;
+ uint32_t style_flags = 0u;
if (xml_parser != nullptr) {
ssize_t idx = xml_parser->indexOfStyle();
if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
if (value.dataType == value.TYPE_ATTRIBUTE) {
- if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
+ // Resolve the attribute with out theme.
+ if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
value.dataType = Res_value::TYPE_NULL;
}
}
+
if (value.dataType == value.TYPE_REFERENCE) {
- style = value.data;
+ style_resid = value.data;
}
}
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
// Retrieve the default style bag, if requested.
- const ResTable::bag_entry* def_style_attr_start = nullptr;
- uint32_t def_style_type_set_flags = 0;
- ssize_t bag_off = def_style_res != 0
- ? res.getBagLocked(def_style_res, &def_style_attr_start,
- &def_style_type_set_flags)
- : -1;
- def_style_type_set_flags |= def_style_bag_type_set_flags;
- const ResTable::bag_entry* const def_style_attr_end =
- def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder def_style_attr_finder(def_style_attr_start,
- def_style_attr_end);
+ const ResolvedBag* default_style_bag = nullptr;
+ if (def_style_resid != 0) {
+ default_style_bag = assetmanager->GetBag(def_style_resid);
+ if (default_style_bag != nullptr) {
+ def_style_flags |= default_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag);
// Retrieve the style class bag, if requested.
- const ResTable::bag_entry* style_attr_start = nullptr;
- uint32_t style_type_set_flags = 0;
- bag_off =
- style != 0
- ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
- : -1;
- style_type_set_flags |= style_bag_type_set_flags;
- const ResTable::bag_entry* const style_attr_end =
- style_attr_start + (bag_off >= 0 ? bag_off : 0);
- BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
+ const ResolvedBag* xml_style_bag = nullptr;
+ if (style_resid != 0) {
+ xml_style_bag = assetmanager->GetBag(style_resid);
+ if (xml_style_bag != nullptr) {
+ style_flags |= xml_style_bag->type_spec_flags;
+ }
+ }
+
+ BagAttributeFinder xml_style_attr_finder(xml_style_bag);
// Retrieve the XML attributes, if requested.
- static const ssize_t kXmlBlock = 0x10000000;
XmlAttributeFinder xml_attr_finder(xml_parser);
- const size_t xml_attr_end =
- xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -289,8 +280,8 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ssize_t block = kXmlBlock;
- uint32_t type_set_flags = 0;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ uint32_t type_set_flags = 0u;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -302,7 +293,7 @@
// Walk through the xml attributes looking for the requested attribute.
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
- if (xml_attr_idx != xml_attr_end) {
+ if (xml_attr_idx != xml_attr_finder.end()) {
// We found the attribute we were looking for.
xml_parser->getAttributeValue(xml_attr_idx, &value);
if (kDebugStyles) {
@@ -312,12 +303,12 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the style class values looking for the requested attribute.
- const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
- if (style_attr_entry != style_attr_end) {
+ const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
+ if (entry != xml_style_attr_finder.end()) {
// We found the attribute we were looking for.
- block = style_attr_entry->stringBlock;
- type_set_flags = style_type_set_flags;
- value = style_attr_entry->map.value;
+ cookie = entry->cookie;
+ type_set_flags = style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -326,25 +317,25 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the default style values looking for the requested attribute.
- const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
- if (def_style_attr_entry != def_style_attr_end) {
+ const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
+ if (entry != def_style_attr_finder.end()) {
// We found the attribute we were looking for.
- block = def_style_attr_entry->stringBlock;
- type_set_flags = style_type_set_flags;
- value = def_style_attr_entry->map.value;
+ cookie = entry->cookie;
+ type_set_flags = def_style_flags;
+ value = entry->value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
- uint32_t resid = 0;
+ uint32_t resid = 0u;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block =
- theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
- if (new_block >= 0) {
- block = new_block;
+ ApkAssetsCookie new_cookie =
+ theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
}
if (kDebugStyles) {
@@ -352,14 +343,15 @@
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
- if (new_block >= 0) {
+ ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+ if (new_cookie != kInvalidCookie) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
- if (new_block >= 0) {
- block = new_block;
+ new_cookie =
+ assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
}
if (kDebugStyles) {
@@ -375,7 +367,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -385,9 +377,7 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -402,36 +392,28 @@
out_values += STYLE_NUM_ENTRIES;
}
- res.unlock();
-
// out_indices must NOT be nullptr.
out_indices[0] = indices_idx;
}
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
- uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices) {
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
ResTable_config config;
Res_value value;
int indices_idx = 0;
- // Now lock down the resource object and start pulling stuff from it.
- res->lock();
-
// Retrieve the XML attributes, if requested.
const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
- static const ssize_t kXmlBlock = 0x10000000;
-
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
- ssize_t block = kXmlBlock;
- uint32_t type_set_flags = 0;
+ ApkAssetsCookie cookie = kInvalidCookie;
+ uint32_t type_set_flags = 0u;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -450,28 +432,27 @@
cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
- uint32_t resid = 0;
+ uint32_t resid = 0u;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- // printf("Resolving attribute reference\n");
- ssize_t new_block = res->resolveReference(&value, block, &resid,
- &type_set_flags, &config);
- if (new_block >= 0) block = new_block;
+ ApkAssetsCookie new_cookie =
+ assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
+ if (new_cookie != kInvalidCookie) {
+ cookie = new_cookie;
+ }
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ cookie = kInvalidCookie;
}
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] =
- block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
- : static_cast<uint32_t>(-1);
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -485,8 +466,6 @@
out_values += STYLE_NUM_ENTRIES;
}
- res->unlock();
-
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 28548e2..e08848f 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -324,8 +324,6 @@
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 f281921..03fad49 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -58,6 +58,7 @@
BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
Iterator Find(uint32_t attr);
+ inline Iterator end();
private:
void JumpToClosestAttribute(uint32_t package_id);
@@ -201,6 +202,11 @@
return end_;
}
+template <typename Derived, typename Iterator>
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() {
+ return end_;
+}
+
} // namespace android
#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 69b76041..35ef98d 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -17,7 +17,8 @@
#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
#define ANDROIDFW_ATTRIBUTERESOLUTION_H
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
namespace android {
@@ -42,19 +43,19 @@
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 965e2db..1775f50 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -45,16 +45,17 @@
// 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 0000000..64924f4
--- /dev/null
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_MUTEXGUARD_H
+#define ANDROIDFW_MUTEXGUARD_H
+
+#include <mutex>
+#include <type_traits>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+template <typename T>
+class ScopedLock;
+
+// Owns the guarded object and protects access to it via a mutex.
+// The guarded object is inaccessible via this class.
+// The mutex is locked and the object accessed via the ScopedLock<T> class.
+//
+// NOTE: The template parameter T should not be a raw pointer, since ownership
+// is ambiguous and error-prone. Instead use an std::unique_ptr<>.
+//
+// Example use:
+//
+// Guarded<std::string> shared_string("hello");
+// {
+// ScopedLock<std::string> locked_string(shared_string);
+// *locked_string += " world";
+// }
+//
+template <typename T>
+class Guarded {
+ static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
+
+ public:
+ explicit Guarded() : guarded_() {
+ }
+
+ template <typename U = T>
+ explicit Guarded(const T& guarded,
+ typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
+ : guarded_(guarded) {
+ }
+
+ template <typename U = T>
+ explicit Guarded(T&& guarded,
+ typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
+ : guarded_(std::move(guarded)) {
+ }
+
+ private:
+ friend class ScopedLock<T>;
+
+ DISALLOW_COPY_AND_ASSIGN(Guarded);
+
+ std::mutex lock_;
+ T guarded_;
+};
+
+template <typename T>
+class ScopedLock {
+ public:
+ explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+ }
+
+ T& operator*() {
+ return guarded_;
+ }
+
+ T* operator->() {
+ return &guarded_;
+ }
+
+ T* get() {
+ return &guarded_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedLock);
+
+ std::lock_guard<std::mutex> lock_;
+ T& guarded_;
+};
+
+} // namespace android
+
+#endif // ANDROIDFW_MUTEXGUARD_H
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 2d73ce8..cc30537 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 @@
class AttributeResolutionTest : public ::testing::Test {
public:
virtual void SetUp() override {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(
- GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents));
- ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(),
- 1 /*cookie*/, true /*copyData*/));
+ styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ ASSERT_NE(nullptr, styles_assets_);
+ assetmanager_.SetApkAssets({styles_assets_.get()});
}
protected:
- ResTable table_;
+ std::unique_ptr<const ApkAssets> styles_assets_;
+ AssetManager2 assetmanager_;
};
class AttributeResolutionXmlTest : public AttributeResolutionTest {
@@ -48,13 +48,12 @@
virtual void SetUp() override {
AttributeResolutionTest::SetUp();
- std::string contents;
- ASSERT_TRUE(
- ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk",
- "res/layout/layout.xml", &contents));
+ std::unique_ptr<Asset> asset =
+ assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
+ ASSERT_NE(nullptr, asset);
- ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(),
- true /*copyData*/));
+ ASSERT_EQ(NO_ERROR,
+ xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/));
// Skip to the first tag.
while (xml_parser_.next() != ResXMLParser::START_TAG) {
@@ -66,14 +65,14 @@
};
TEST_F(AttributeResolutionTest, Theme) {
- ResTable::Theme theme(table_);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+ std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+ ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
attrs.size(), values.data(), nullptr /*out_indices*/));
@@ -126,8 +125,8 @@
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
- nullptr /*out_indices*/));
+ ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
+ values.data(), nullptr /*out_indices*/));
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -171,15 +170,15 @@
}
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
- ResTable::Theme theme(table_);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+ std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
+ ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
attrs.size(), values.data(), indices.data());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index 7149bee..a8abcb5 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -33,12 +33,12 @@
}
}
+ // 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 98e9a42..e70d5ea 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -18,9 +18,11 @@
#include <utils/Log.h>
#include <android/asset_manager_jni.h>
+#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <utils/threads.h>
#include "jni.h"
@@ -35,21 +37,20 @@
// -----
struct AAssetDir {
- AssetDir* mAssetDir;
+ std::unique_ptr<AssetDir> mAssetDir;
size_t mCurFileIndex;
String8 mCachedFileName;
- explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { }
- ~AAssetDir() { delete mAssetDir; }
+ explicit AAssetDir(std::unique_ptr<AssetDir> dir) :
+ mAssetDir(std::move(dir)), mCurFileIndex(0) { }
};
// -----
struct AAsset {
- Asset* mAsset;
+ std::unique_ptr<Asset> mAsset;
- explicit AAsset(Asset* asset) : mAsset(asset) { }
- ~AAsset() { delete mAsset; }
+ explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { }
};
// -------------------- Public native C API --------------------
@@ -104,19 +105,18 @@
return NULL;
}
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- Asset* asset = mgr->open(filename, amMode);
- if (asset == NULL) {
- return NULL;
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode);
+ if (asset == nullptr) {
+ return nullptr;
}
-
- return new AAsset(asset);
+ return new AAsset(std::move(asset));
}
AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName)
{
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- return new AAssetDir(mgr->openDir(dirName));
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ return new AAssetDir(locked_mgr->OpenDir(dirName));
}
/**
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b32be73..52d0e08e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -24,8 +24,9 @@
#include <utils/misc.h>
#include <inttypes.h>
+#include <android-base/macros.h>
#include <androidfw/Asset.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <androidfw/ResourceTypes.h>
#include <android-base/macros.h>
@@ -1664,18 +1665,22 @@
static jlong
nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
- jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset);
+ jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release());
return id;
}
@@ -1752,22 +1757,25 @@
nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path,
jfloat fontSize, jint dpi)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con,
str.c_str(), str.length(),
fontSize, dpi,
asset->getBuffer(false), asset->getLength());
- delete asset;
return id;
}