diff options
25 files changed, 2501 insertions, 2880 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d5c88aa1fbcb..3d26af1f15bb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,7 +54,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1595,19 +1594,21 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; + AssetManager assets = null; XmlResourceParser parser = null; try { - try { - apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, false, false) - : ApkAssets.loadFromPath(apkPath); - } catch (IOException e) { + assets = newConfiguredAssetManager(); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); + if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1635,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + IoUtils.closeQuietly(assets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java deleted file mode 100644 index b087c48d8d4c..000000000000 --- a/core/java/android/content/res/ApkAssets.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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 78370f4d56a8..78665609bdd4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,11 +18,9 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; -import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -30,18 +28,10 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.BufferedReader; -import java.io.FileInputStream; +import java.io.FileDescriptor; 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; /** @@ -52,17 +42,7 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - 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]; + /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how @@ -85,279 +65,87 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - @GuardedBy("this") private final TypedValue mValue = new TypedValue(); - @GuardedBy("this") private final long[] mOffsets = new long[2]; - - // Pointer to native implementation, stuffed inside a long. - @GuardedBy("this") private long mObject; - - // The loaded asset paths. - @GuardedBy("this") private ApkAssets[] mApkAssets; + 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; - // Debug/reference counting implementation. - @GuardedBy("this") private boolean mOpen = true; - @GuardedBy("this") private int mNumRefs = 1; - @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks; + private final TypedValue mValue = new TypedValue(); + private final long[] mOffsets = new long[2]; + + // For communication with native code. + private long mObject; + private StringBlock mStringBlocks[] = null; + + private int mNumRefs = 1; + private boolean mOpen = true; + 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() { - final ApkAssets[] assets; - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - assets = sSystemApkAssets; - } - - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + init(false); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); + ensureSystemAssets(); } - - // 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; - } - - // 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*/)); - } - } + + private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); } - - 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() { - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - return sSystem; - } + ensureSystemAssets(); + return sSystem; } /** * Close this asset manager. */ - @Override public void close() { - synchronized (this) { - if (!mOpen) { - return; - } - - mOpen = false; - decRefsLocked(hashCode()); - } - } - - /** - * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} - * family of methods. - * - * @param apkAssets The new set of paths. - * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. - * Set this to false if you are appending new resources - * (not new configurations). - * @hide - */ - public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { - Preconditions.checkNotNull(apkAssets, "apkAssets"); - synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); - if (invalidateCaches) { - // Invalidate all caches. - invalidateCachesLocked(-1); - } - } - } - - /** - * Invalidates the caches in this AssetManager according to the bitmask `diff`. - * - * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. - * @see ActivityInfo.Config - */ - private void invalidateCachesLocked(int diff) { - // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. - } - - /** - * @hide - */ - public @NonNull ApkAssets[] getApkAssets() { - synchronized (this) { - ensureValidLocked(); - return mApkAssets; - } - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPath(String path) { - return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addOverlayPath(String path) { - return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); - } - - private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureOpenLocked(); - final int count = mApkAssets.length; - for (int i = 0; i < count; i++) { - if (mApkAssets[i].getAssetPath().equals(path)) { - return i + 1; - } - } - - final ApkAssets assets; - try { - if (overlay) { - // TODO(b/70343104): This hardcoded path will be removed once - // addAssetPathInternal is deleted. - final String idmapPath = "/data/resource-cache/" - + path.substring(1).replace('/', '@') - + "@idmap"; - assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); - } else { - assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); - } - } catch (IOException e) { - return 0; + synchronized(this) { + //System.out.println("Release: num=" + mNumRefs + // + ", released=" + mReleased); + if (mOpen) { + mOpen = false; + decRefsLocked(this.hashCode()); } - - final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); - newApkAssets[count] = assets; - setApkAssets(newApkAssets, true); - return count + 1; - } - } - - /** - * Ensures that the native implementation has not been destroyed. - * The AssetManager may have been closed, but references to it still exist - * and therefore the native implementation is not destroyed. - */ - private void ensureValidLocked() { - if (mObject == 0) { - throw new RuntimeException("AssetManager has been destroyed"); - } - } - - /** - * Ensures that the AssetManager has not been explicitly closed. If this method passes, - * then this implies that ensureValidLocked() also passes. - */ - private void ensureOpenLocked() { - if (!mOpen) { - throw new RuntimeException("AssetManager has been closed"); - } - } - - /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. - * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise - */ - boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeGetResourceValue( - mObject, resId, (short) densityDpi, outValue, resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; } } @@ -368,7 +156,8 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceText(@StringRes int resId) { + @Nullable + final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -383,15 +172,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId the index into the bag to load + * @param bagEntryId * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable + final 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) { + final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); + if (block < 0) { return null; } @@ -400,60 +189,52 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mApkAssets[cookie - 1].getStringFromPool(outValue.data); + return mStringBlocks[block].get(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. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @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 + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { - Preconditions.checkNotNull(outData, "outData"); - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArray(mObject, resId, outData); - } + @Nullable + final String[] getResourceStringArray(@ArrayRes int resId) { + return getArrayStringResource(resId); } /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise */ - @Nullable String[] getResourceStringArray(@ArrayRes int resId) { + final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceStringArray(mObject, resId); + final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); + if (block < 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mStringBlocks[block].get(outValue.data); + } + return true; } } @@ -463,48 +244,26 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - ensureValidLocked(); - final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); + final int[] rawInfoArray = getArrayStringInfo(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++) { - int cookie = rawInfoArray[i]; - int index = rawInfoArray[i + 1]; - retArray[j] = (index >= 0 && cookie > 0) - ? mApkAssets[cookie - 1].getStringFromPool(index) : null; + block = rawInfoArray[i]; + index = rawInfoArray[i + 1]; + retArray[j] = index >= 0 ? mStringBlocks[block].get(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 @@ -518,88 +277,73 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, - resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - - void dumpTheme(long theme, int priority, String tag, String prefix) { - synchronized (this) { - ensureValidLocked(); - nativeThemeDump(mObject, theme, priority, tag, prefix); + final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); + if (block < 0) { + return false; } - } - @Nullable String getResourceName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceName(mObject, resId); - } - } + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); - @Nullable String getResourcePackageName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourcePackageName(mObject, resId); + if (outValue.type == TypedValue.TYPE_STRING) { + final StringBlock[] blocks = ensureStringBlocks(); + outValue.string = blocks[block].get(outValue.data); } + return true; } - @Nullable String getResourceTypeName(@AnyRes int resId) { + /** + * Ensures the string blocks are loaded. + * + * @return the string blocks + */ + @NonNull + final StringBlock[] ensureStringBlocks() { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceTypeName(mObject, resId); + if (mStringBlocks == null) { + makeStringBlocks(sSystem.mStringBlocks); + } + return mStringBlocks; } } - @Nullable String getResourceEntryName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceEntryName(mObject, resId); + /*package*/ final void makeStringBlocks(StringBlock[] seed) { + final int seedNum = (seed != null) ? seed.length : 0; + final int num = getStringBlockCount(); + mStringBlocks = new StringBlock[num]; + if (localLOGV) Log.v(TAG, "Making string blocks for " + this + + ": " + num); + for (int i=0; i<num; i++) { + if (i < seedNum) { + mStringBlocks[i] = seed[i]; + } else { + mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); + } } } - @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType, - @Nullable String defPackage) { + /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) { synchronized (this) { - ensureValidLocked(); - // name is checked in JNI. - return nativeGetResourceIdentifier(mObject, name, defType, defPackage); + // Cookies map to string blocks starting at 1. + return mStringBlocks[cookie - 1].get(id); } } - 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 @NonNull InputStream open(@NonNull String fileName) throws IOException { + public final InputStream open(String fileName) throws IOException { return open(fileName, ACCESS_STREAMING); } @@ -609,7 +353,8 @@ public final class AssetManager implements AutoCloseable { * 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 @@ -619,40 +364,34 @@ public final class AssetManager implements AutoCloseable { * @see #open(String) * @see #list */ - public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + public final InputStream open(String fileName, int accessMode) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long asset = nativeOpenAsset(mObject, fileName, accessMode); - if (asset == 0) { - throw new FileNotFoundException("Asset file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + 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); } - /** - * 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"); + public final AssetFileDescriptor openFd(String fileName) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); - if (pfd == null) { - throw new FileNotFoundException("Asset file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + 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); } /** @@ -667,121 +406,90 @@ public final class AssetManager implements AutoCloseable { * * @see #open */ - public @Nullable String[] list(@NonNull String path) throws IOException { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureValidLocked(); - return nativeList(mObject, path); - } - } + public native final String[] list(String path) + throws IOException; /** + * {@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 @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException { + public final InputStream openNonAsset(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 @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode) - throws IOException { + public final InputStream openNonAsset(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 @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName) - throws IOException { + public final InputStream openNonAsset(int cookie, 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 @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode) - throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + public final InputStream openNonAsset(int cookie, String fileName, int accessMode) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); - if (asset == 0) { - throw new FileNotFoundException("Asset absolute file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + 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); } - /** - * 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) + public final AssetFileDescriptor openNonAssetFd(String fileName) throws IOException { return openNonAssetFd(0, 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 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"); + + public final AssetFileDescriptor openNonAssetFd(int cookie, + String fileName) throws IOException { synchronized (this) { - ensureOpenLocked(); - final ParcelFileDescriptor pfd = - nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); - if (pfd == null) { - throw new FileNotFoundException("Asset absolute file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + 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); } /** @@ -789,7 +497,7 @@ public final class AssetManager implements AutoCloseable { * * @param fileName The name of the file to retrieve. */ - public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName) + public final XmlResourceParser openXmlResourceParser(String fileName) throws IOException { return openXmlResourceParser(0, fileName); } @@ -800,265 +508,270 @@ public final class AssetManager implements AutoCloseable { * @param cookie Identifier of the package to be opened. * @param fileName The name of the file to retrieve. */ - 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; - } + public final XmlResourceParser openXmlResourceParser(int cookie, + String fileName) throws IOException { + XmlBlock block = openXmlBlockAsset(cookie, fileName); + XmlResourceParser rp = block.newParser(); + block.close(); + return rp; } /** - * Retrieve a non-asset as a compiled XML file. Not for use by applications. + * {@hide} + * Retrieve a non-asset as a compiled XML file. Not for use by + * applications. * * @param fileName The name of the file to retrieve. - * @hide */ - @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException { + /*package*/ final XmlBlock openXmlBlockAsset(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 */ - @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); - if (xmlBlock == 0) { - throw new FileNotFoundException("Asset XML file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + 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); } - void xmlBlockGone(int id) { + /*package*/ void xmlBlockGone(int id) { synchronized (this) { decRefsLocked(id); } } - 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) { - // 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); - } - } - - 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) { - // 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) { + /*package*/ final long createTheme() { synchronized (this) { - nativeThemeDestroy(themePtr); - decRefsLocked(themePtr); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + long res = newTheme(); + incRefsLocked(res); + return res; } } - void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) { + /*package*/ final void releaseTheme(long theme) { synchronized (this) { - // Need to synchronize on AssetManager because we will be accessing - // the native implementation of AssetManager. - ensureValidLocked(); - nativeThemeApplyStyle(mObject, themePtr, resId, force); + deleteTheme(theme); + decRefsLocked(theme); } } - @Override protected void finalize() throws Throwable { - 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); + 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 (mObject != 0) { - nativeDestroy(mObject); + destroy(); + } finally { + super.finalize(); } } - - /* 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 mAssetNativePtr; + return mAsset; } - - private AssetInputStream(long assetNativePtr) { - mAssetNativePtr = assetNativePtr; - mLength = nativeAssetGetLength(assetNativePtr); + private AssetInputStream(long asset) + { + mAsset = asset; + mLength = getAssetLength(asset); } - - @Override public final int read() throws IOException { - ensureOpen(); - return nativeAssetReadChar(mAssetNativePtr); + return readAssetChar(mAsset); } - - @Override - public final int read(@NonNull byte[] b) throws IOException { - ensureOpen(); - Preconditions.checkNotNull(b, "b"); - return nativeAssetRead(mAssetNativePtr, b, 0, b.length); + public final boolean markSupported() { + return true; } - - @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 int available() throws IOException { + long len = getAssetRemainingLength(mAsset); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + } + public final void close() throws IOException { + synchronized (AssetManager.this) { + if (mAsset != 0) { + destroyAsset(mAsset); + mAsset = 0; + decRefsLocked(hashCode()); + } + } + } + public final void mark(int readlimit) { + mMarkPos = seekAsset(mAsset, 0, 0); + } + 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 { - ensureOpen(); - long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); - if ((pos + n) > mLength) { - n = mLength - pos; + long pos = seekAsset(mAsset, 0, 0); + if ((pos+n) > mLength) { + n = mLength-pos; } if (n > 0) { - nativeAssetSeek(mAssetNativePtr, n, 0); + seekAsset(mAsset, n, 0); } return n; } - @Override - public final int available() throws IOException { - ensureOpen(); - final long len = nativeAssetGetRemainingLength(mAssetNativePtr); - return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; + protected void finalize() throws Throwable + { + close(); } - @Override - public final boolean markSupported() { - return true; - } + private long mAsset; + private long mLength; + private long mMarkPos; + } - @Override - public final void mark(int readlimit) { - ensureOpen(); - mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); + /** + * Add an additional set of assets to the asset manager. This can be + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPath(String path) { + return addAssetPathInternal(path, false); + } + + /** + * Add an application assets to the asset manager and loading it as shared library. + * This can be either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, true); + } + + private final int addAssetPathInternal(String path, boolean appAsLib) { + synchronized (this) { + int res = addAssetPathNative(path, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void reset() throws IOException { - ensureOpen(); - nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); + private native final int addAssetPathNative(String path, boolean appAsLib); + + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void close() throws IOException { - if (mAssetNativePtr != 0) { - nativeAssetDestroy(mAssetNativePtr); - mAssetNativePtr = 0; + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); - synchronized (AssetManager.this) { - decRefsLocked(hashCode()); - } - } + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + + public final int addOverlayPath(String idmapPath) { + synchronized (this) { + int res = addOverlayPathNative(idmapPath); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - protected void finalize() throws Throwable { - close(); + /** + * 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; } - private void ensureOpen() { - if (mAssetNativePtr == 0) { - throw new IllegalStateException("AssetInputStream is closed"); - } + 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 boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { - return false; - } - } - return true; - } + public native final boolean isUpToDate(); /** * Get the locales that this asset manager contains data for. @@ -1071,12 +784,7 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public String[] getLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, false /*excludeSystem*/); - } - } + public native final String[] getLocales(); /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -1086,57 +794,131 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * @hide + * {@hide} */ - public String[] getNonSystemLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, true /*excludeSystem*/); - } - } + public native final String[] getNonSystemLocales(); - /** - * @hide - */ - Configuration[] getSizeConfigurations() { - synchronized (this) { - ensureValidLocked(); - return nativeGetSizeConfigurations(mObject); - } - } + /** {@hide} */ + public native final Configuration[] getSizeConfigurations(); /** - * Change the configuration used when retrieving resources. Not for use by + * Change the configuation used when retrieving resources. Not for use by * applications. - * @hide + * {@hide} */ - 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); - } - } + 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); /** - * @hide + * Retrieve the resource identifier for the given resource name. */ - public SparseArray<String> getAssignedPackageIdentifiers() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssignedPackageIdentifiers(mObject); - } - } + /*package*/ native final int getResourceIdentifier(String name, + String defType, + String defPackage); - private void incRefsLocked(long id) { + /*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) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<>(); + mRefStacks = new HashMap<Long, RuntimeException>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -1144,117 +926,16 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private void decRefsLocked(long id) { + + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - if (mNumRefs == 0 && mObject != 0) { - nativeDestroy(mObject); - mObject = 0; + //System.out.println("Dec streams: mNumRefs=" + mNumRefs + // + " mReleased=" + mReleased); + if (mNumRefs == 0) { + destroy(); } } - - // 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 8f58891ed556..e173653cd961 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); + int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getResourceArraySize(id); + int len = impl.getAssets().getArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().getResourceArray(id, array.mData); + array.mLength = impl.getAssets().retrieveArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,7 +1789,8 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, 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 2a4b278bdca8..97cb78bc4243 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,6 +168,7 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); + mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1273,7 +1274,8 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); + mThemeResId = resId; mKey.append(resId, force); } @@ -1282,7 +1284,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1305,10 +1307,12 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, - array.mDataAddress, array.mIndicesAddress); + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; + return array; } } @@ -1325,7 +1329,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1345,14 +1349,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.nativeThemeGetChangingConfigurations(mTheme); + AssetManager.getThemeChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - mAssets.dumpTheme(mTheme, priority, tag, prefix); + AssetManager.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1381,13 +1385,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.nativeThemeClear(mTheme); + AssetManager.clearTheme(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]; - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index cbb3c6df0558..f33c75168a5f 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,15 +61,6 @@ public class TypedArray { return attrs; } - // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; - static final int STYLE_TYPE = 0; - static final int STYLE_DATA = 1; - static final int STYLE_ASSET_COOKIE = 2; - static final int STYLE_RESOURCE_ID = 3; - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - static final int STYLE_DENSITY = 5; - private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -87,7 +78,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * STYLE_NUM_ENTRIES; + final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -175,9 +166,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -212,9 +203,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -251,13 +242,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString(data[index + STYLE_DATA]).toString(); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]).toString(); } } return null; @@ -282,11 +274,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -328,14 +320,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA] != 0; + return data[index+AssetManager.STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -367,14 +359,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -404,16 +396,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index + STYLE_DATA]); + return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -454,15 +446,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -506,7 +498,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -541,7 +533,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -572,15 +564,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -620,14 +612,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimension( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -668,14 +661,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -717,14 +711,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -760,15 +755,16 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -799,14 +795,15 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index + AssetManager.STYLE_DATA], mMetrics); } return defValue; @@ -836,14 +833,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); + return TypedValue.complexToFraction( + data[index+AssetManager.STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -876,10 +874,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index + STYLE_RESOURCE_ID]; + if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -904,10 +902,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + STYLE_DATA]; + if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + AssetManager.STYLE_DATA]; } return defValue; } @@ -941,7 +939,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -977,7 +975,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1008,7 +1006,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1029,7 +1027,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index * STYLE_NUM_ENTRIES, outValue); + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -1045,8 +1043,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; - return mData[index + STYLE_TYPE]; + index *= AssetManager.STYLE_NUM_ENTRIES; + return mData[index + AssetManager.STYLE_TYPE]; } /** @@ -1065,9 +1063,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1086,11 +1084,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1111,7 +1109,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1183,16 +1181,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + STYLE_DATA]; + final int attr = data[index + AssetManager.STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1233,44 +1231,45 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - final int type = data[index + STYLE_TYPE]; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index + STYLE_DATA]; - outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index + STYLE_RESOURCE_ID]; + outValue.data = data[index+AssetManager.STYLE_DATA]; + outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index + STYLE_DENSITY]; + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index+AssetManager.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 + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString(data[index + STYLE_DATA]); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index d4ccffb83ca5..e6b957414ea8 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,7 +16,6 @@ package android.content.res; -import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -34,7 +33,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock implements AutoCloseable { +final class XmlBlock { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -49,7 +48,6 @@ final class XmlBlock implements AutoCloseable { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } - @Override public void close() { synchronized (this) { if (mOpen) { @@ -480,13 +478,13 @@ final class XmlBlock implements AutoCloseable { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(@Nullable AssetManager assets, long xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private @Nullable final AssetManager mAssets; + private 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 5751fc99a484..93f95c634ae3 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_Log.cpp", "android_util_MemoryIntArray.cpp", + "android_util_Log.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -190,7 +190,6 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", - "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e3b5c8f3136a..aa9a82415f97 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,7 +122,6 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); -extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1341,7 +1340,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), - REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index c50026ea570e..dd3e6f02e9fe 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/AssetManager2.h> +#include <androidfw/AssetManager.h> #include "Utils.h" #include "FontUtils.h" @@ -224,8 +224,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr); + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -237,33 +236,27 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - 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); - } + 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); } - if (nullptr == asset) { + if (NULL == 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.release())); + sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); 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 49a24a30f77e..09e37e1a3de6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); + code->assetManager = assetManagerForJavaObject(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 deleted file mode 100644 index c0f151b71c93..000000000000 --- a/core/jni/android_content_res_ApkAssets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 7e17a4909cee..683b4c490ec3 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1441 +1,1851 @@ -/* - * 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. - */ +/* //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. +*/ #define LOG_TAG "asset" +#include <android_runtime/android_util_AssetManager.h> + #include <inttypes.h> #include <linux/capability.h> #include <stdio.h> -#include <sys/stat.h> -#include <sys/system_properties.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/stat.h> +#include <sys/system_properties.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM -#include "android-base/logging.h" -#include "android-base/properties.h" -#include "android-base/stringprintf.h" -#include "android_runtime/android_util_AssetManager.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" -#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" -#include "nativehelper/ScopedPrimitiveArray.h" -#include "nativehelper/ScopedStringChars.h" -#include "nativehelper/ScopedUtfChars.h" +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedUtfChars.h> #include "utils/Log.h" -#include "utils/String8.h" #include "utils/misc.h" +#include "utils/String8.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 { - jfieldID native_ptr; -} gApkAssetsFields; - -static struct sparsearray_offsets_t { - jclass classObject; - jmethodID constructor; - jmethodID put; +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 = nullptr; +jclass g_stringClass = 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; -} - -constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { - return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie; +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + static_cast<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; } // This is called by zygote (running as user root) as part of preloadResources. -static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { - switch (pid_t pid = fork()) { - case -1: - PLOG(ERROR) << "failed to fork for idmap"; - break; - - // child - case 0: { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capget"; - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capset"; - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setgid"; - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setuid"; - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, 0, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/<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(); - } - } - - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - PLOG(ERROR) << "failed to execv for idmap"; - exit(1); // should never get here - } else { - exit(0); - } - } break; - - // parent - default: - waitpid(pid, nullptr, 0); - break; - } +static void verifySystemIdmaps() +{ + pid_t pid; + char system_id[10]; + + snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); + + switch (pid = fork()) { + case -1: + ALOGE("failed to fork for idmap: %s", strerror(errno)); + break; + case 0: // child + { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + ALOGE("capget: %s\n", strerror(errno)); + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + ALOGE("capset: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + ALOGE("setgid: %s\n", strerror(errno)); + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + ALOGE("setuid: %s\n", strerror(errno)); + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, NULL, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR. + char subdir[PROP_VALUE_MAX]; + int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); + if (len > 0) { + String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; + if (stat(overlayPath.string(), &st) == 0) { + argv[argc++] = overlayPath.string(); + } + } + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + ALOGE("failed to execv for idmap: %s", strerror(errno)); + exit(1); // should never get here + } else { + exit(0); + } + } + break; + default: // parent + waitpid(pid, NULL, 0); + break; + } } -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)); -} // ---------------------------------------------------------------------------- -// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. -struct GuardedAssetManager : public ::AAssetManager { - Guarded<AssetManager2> guarded_assetmanager; -}; - -::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); - ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); - if (am == nullptr) { +// 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 nullptr; - } - return am; + return NULL; } -Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { - if (assetmanager == nullptr) { - return nullptr; - } - return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager; -} +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); -Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); + return reinterpret_cast<jlong>(a); } -static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) { - return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr)); +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 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(); - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably " - "compressed"); - return nullptr; - } - - jlong* offsets = reinterpret_cast<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 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 jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { - return Asset::getGlobalCount(); +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 NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return nullptr; - } - return env->NewStringUTF(alloc.string()); +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 jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { - // TODO(adamlesinski): Switch to AssetManager2. - return AssetManager::getGlobalCount(); +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 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 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 void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast<GuardedAssetManager*>(ptr); +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 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; +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; } - jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); - if (env->ExceptionCheck()) { - return; + 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; } - apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr)); - } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetApkAssets(apk_assets, invalidate_caches); + 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 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 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 jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); - jobject sparse_array = - env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } - if (sparse_array == nullptr) { - // An exception is pending. - return nullptr; - } + return a->getLength(); +} - 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; +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; } - env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id), - jpackage_name); - }); - return sparse_array; + return a->getRemainingLength(); } -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()); - } - }); +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; } - } - jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } - jsize index = 0; - for (const std::string& file_path : all_file_paths) { - jstring java_string = env->NewStringUTF(file_path.c_str()); + int32_t cookie; + bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; + 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; } - env->SetObjectArrayElement(array, index++, java_string); + int32_t cookie; + bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - // 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; + return (res) ? (jint)cookie : 0; } -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 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 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 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 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 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 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 jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); } -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 jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); } -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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; - } +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } - const jsize out_data_length = env->GetArrayLength(out_data); - if (env->ExceptionCheck()) { - return -1; - } + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } - 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; - } + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } - 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); + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; } -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 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 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; - } +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); + } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } + return static_cast<jint>(block); +} - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; +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; - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + 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++; } - } - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; + res.unlock(); + + if (block < 0) { + return static_cast<jint>(block); } - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); } - } - return env->NewStringUTF(result.c_str()); + + return static_cast<jint>(block); } -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 jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return am->getResources().getTableCount(); } -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 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 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 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 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 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 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 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 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*/); +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + delete theme; +} - jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); - if (array == nullptr) { - return nullptr; - } +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); +} - size_t idx = 0; - for (const ResTable_config& configuration : configurations) { - jobject java_configuration = ConstructConfigurationObject(env, configuration); - if (java_configuration == nullptr) { - return nullptr; +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; +} - env->SetObjectArrayElement(array, idx++, java_configuration); - env->DeleteLocalRef(java_configuration); - } - return array; +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 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 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 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); +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; - } - } - } - - 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; -} + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return 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); + 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; - } } - } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } - 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)); + jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); + const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); - 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; -} + 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); + } + } -static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - return reinterpret_cast<jlong>(assetmanager->NewTheme().release()); + 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 NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - delete reinterpret_cast<Theme*>(theme_ptr); +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 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 jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jlong xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0) { + jniThrowNullPointerException(env, "xmlParserToken"); + return JNI_FALSE; + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + bool result = RetrieveAttributes(&res, xmlParser, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void 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 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 void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - reinterpret_cast<Theme*>(theme_ptr)->Clear(); +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 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 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 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 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 jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, - jlong theme_ptr) { - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - return static_cast<jint>(theme->GetChangingConfigurations()); +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 void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - delete reinterpret_cast<Asset*>(asset_ptr); +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 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 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 jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, - jint offset, jint len) { - if (len == 0) { - return 0; - } +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; + } - 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; - } + am->addDefaultAssets(); - ScopedByteArrayRW byte_array(env, java_buffer); - if (byte_array.get() == nullptr) { - return -1; - } + ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am)); +} - 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 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 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 jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); } -static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - return static_cast<jlong>(asset->getLength()); +static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) +{ + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return NULL; + } + + jstring str = env->NewStringUTF(alloc.string()); + return str; } -static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - return static_cast<jlong>(asset->getRemainingLength()); +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); } // ---------------------------------------------------------------------------- -// JNI registration. +/* + * 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}, + /* 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 }, }; -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)); +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", + "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, + "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", + "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, + "<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 2c1e3579eb92..8dd933707a6a 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_RUNTIME_ASSETMANAGER_H -#define ANDROID_RUNTIME_ASSETMANAGER_H +#ifndef android_util_AssetManager_H +#define android_util_AssetManager_H -#include "androidfw/AssetManager2.h" -#include "androidfw/MutexGuard.h" +#include <androidfw/AssetManager.h> #include "jni.h" namespace android { -extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); +extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); -} // namespace android +} -#endif // ANDROID_RUNTIME_ASSETMANAGER_H +#endif diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,6 +265,8 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { + ATRACE_CALL(); + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -427,7 +429,9 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - *out_last_reference = in_out_value->data; + if (out_last_reference != nullptr) { + *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 f912af4f7190..60e3845d98a9 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,18 +20,13 @@ #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: @@ -49,53 +44,58 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { + : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> { public: - BagAttributeFinder(const ResolvedBag* bag) - : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, - bag != nullptr ? bag->entries + bag->entry_count : nullptr) { - } + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} - inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { - return entry->key; + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; } }; -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) { +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) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Retrieve the default style bag, if requested. - 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; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // 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); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; + ssize_t block = -1; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,14 +122,15 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, 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 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; + 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; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -139,26 +140,22 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; 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! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + // 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 (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -172,7 +169,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = -1; } if (kDebugStyles) { @@ -182,7 +179,9 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -196,80 +195,90 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -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, +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, 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_resid, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", + theme, def_style_attr, def_style_res, xml_parser); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; + def_style_res = value.data; } } } - // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; 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) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { value.dataType = Res_value::TYPE_NULL; } } - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; + style = value.data; } } } - // Retrieve the default style bag, if requested. - 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; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // 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); // Retrieve the style class bag, if requested. - 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); + 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); // 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. @@ -280,8 +289,8 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -293,7 +302,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // 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_finder.end()) { + if (xml_attr_idx != xml_attr_end) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -303,12 +312,12 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, 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 ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); - if (entry != xml_style_attr_finder.end()) { + const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); + if (style_attr_entry != style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + block = style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -317,25 +326,25 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, 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 ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { + 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) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + block = def_style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = def_style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -343,15 +352,14 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } } 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! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -367,7 +375,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } if (kDebugStyles) { @@ -377,7 +385,9 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -392,28 +402,36 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(const ResTable* res, 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]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -432,27 +450,28 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + // 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; } // 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; - cookie = kInvalidCookie; + block = kXmlBlock; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -466,6 +485,8 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u 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 e08848f891f6..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,6 +324,8 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { + ATRACE_CALL(); + // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index 03fad4947dfe..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,7 +58,6 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); - inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -202,11 +201,6 @@ Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) { return end_; } -template <typename Derived, typename Iterator> -Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() { - return end_; -} - } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..69b760414846 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,8 +17,7 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" +#include <androidfw/ResourceTypes.h> namespace android { @@ -43,19 +42,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, +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); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -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, +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, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(const ResTable* res, 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 1775f5070f4e..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,17 +45,16 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + const ResTable_entry* entry = 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; + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; + const DynamicRefTable* dynamic_ref_table = nullptr; // 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 deleted file mode 100644 index 64924f433245..000000000000 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 cc3053798e7b..2d73ce8f8ee3 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,7 +21,6 @@ #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" @@ -33,14 +32,15 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - ASSERT_NE(nullptr, styles_assets_); - assetmanager_.SetApkAssets({styles_assets_.get()}); + 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*/)); } protected: - std::unique_ptr<const ApkAssets> styles_assets_; - AssetManager2 assetmanager_; + ResTable table_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,12 +48,13 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::unique_ptr<Asset> asset = - assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, asset); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); - ASSERT_EQ(NO_ERROR, - xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,14 +66,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, 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.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -125,8 +126,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&table_, &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]); @@ -170,15 +171,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, 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.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*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 a8abcb5df86c..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTab } } - // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. - const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } + const ResTable& table = assetmanager.getResources(true); + Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index e70d5ea0d566..98e9a42d944d 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,11 +18,9 @@ #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" @@ -37,20 +35,21 @@ using namespace android; // ----- struct AAssetDir { - std::unique_ptr<AssetDir> mAssetDir; + AssetDir* mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(std::unique_ptr<AssetDir> dir) : - mAssetDir(std::move(dir)), mCurFileIndex(0) { } + explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } + ~AAssetDir() { delete mAssetDir; } }; // ----- struct AAsset { - std::unique_ptr<Asset> mAsset; + Asset* mAsset; - explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { } + explicit AAsset(Asset* asset) : mAsset(asset) { } + ~AAsset() { delete mAsset; } }; // -------------------- Public native C API -------------------- @@ -105,18 +104,19 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode); - if (asset == nullptr) { - return nullptr; + AssetManager* mgr = static_cast<AssetManager*>(amgr); + Asset* asset = mgr->open(filename, amMode); + if (asset == NULL) { + return NULL; } - return new AAsset(std::move(asset)); + + return new AAsset(asset); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - return new AAssetDir(locked_mgr->OpenDir(dirName)); + AssetManager* mgr = static_cast<AssetManager*>(amgr); + return new AAssetDir(mgr->openDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 52d0e08e4e7f..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,9 +24,8 @@ #include <utils/misc.h> #include <inttypes.h> -#include <android-base/macros.h> #include <androidfw/Asset.h> -#include <androidfw/AssetManager2.h> +#include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> #include <android-base/macros.h> @@ -1665,22 +1664,18 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - 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; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); return id; } @@ -1757,25 +1752,22 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - 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; - } + Asset* asset = 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; } |