diff options
38 files changed, 889 insertions, 121 deletions
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp index c5fc941..1153f38 100644 --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -119,7 +119,8 @@ namespace { int parse_manifest(const void *data, size_t size, const char *target_package_name) { - ResXMLTree parser(data, size); + ResXMLTree parser; + parser.setTo(data, size); if (parser.getError() != NO_ERROR) { ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError()); return -1; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 69ada6a..965f815 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1592,10 +1592,10 @@ public final class ActivityThread { /** * Creates the top level resources for the given package. */ - Resources getTopLevelResources(String resDir, String[] overlayDirs, + Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { - return mResourcesManager.getTopLevelResources(resDir, overlayDirs, displayId, + return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 8165fa1..0615bd9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -805,7 +805,7 @@ final class ApplicationPackageManager extends PackageManager { } Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, - app.resourceDirs, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); + app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); if (r != null) { return r; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 87f47a1..589c82f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2044,8 +2044,9 @@ class ContextImpl extends Context { || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources( - packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId, - overrideConfiguration, compatInfo, activityToken); + packageInfo.getResDir(), packageInfo.getOverlayDirs(), + packageInfo.getApplicationInfo().sharedLibraryFiles, + displayId, overrideConfiguration, compatInfo, activityToken); } } mResources = resources; diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index d409352..3ae8bfc 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -41,6 +41,7 @@ import android.os.Trace; import android.os.UserHandle; import android.util.AndroidRuntimeException; import android.util.Slog; +import android.util.SparseArray; import android.view.DisplayAdjustments; import android.view.Display; @@ -48,6 +49,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.net.URL; import java.util.Enumeration; @@ -485,7 +488,7 @@ public final class LoadedApk { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs, - Display.DEFAULT_DISPLAY, null, this); + mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); } return mResources; } @@ -530,10 +533,101 @@ public final class LoadedApk { } } } - + + // Rewrite the R 'constants' for all library apks. + SparseArray<String> packageIdentifiers = getAssets(mActivityThread) + .getAssignedPackageIdentifiers(); + final int N = packageIdentifiers.size(); + for (int i = 0; i < N; i++) { + final int id = packageIdentifiers.keyAt(i); + if (id == 0x01 || id == 0x7f) { + continue; + } + + rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id); + } + return app; } + private void rewriteIntField(Field field, int packageId) throws IllegalAccessException { + int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; + int bannedModifiers = Modifier.FINAL; + + int mod = field.getModifiers(); + if ((mod & requiredModifiers) != requiredModifiers || + (mod & bannedModifiers) != 0) { + throw new IllegalArgumentException("Field " + field.getName() + + " is not rewritable"); + } + + if (field.getType() != int.class && field.getType() != Integer.class) { + throw new IllegalArgumentException("Field " + field.getName() + + " is not an integer"); + } + + try { + int resId = field.getInt(null); + field.setInt(null, (resId & 0x00ffffff) | (packageId << 24)); + } catch (IllegalAccessException e) { + // This should not occur (we check above if we can write to it) + throw new IllegalArgumentException(e); + } + } + + private void rewriteIntArrayField(Field field, int packageId) { + int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; + + if ((field.getModifiers() & requiredModifiers) != requiredModifiers) { + throw new IllegalArgumentException("Field " + field.getName() + + " is not rewritable"); + } + + if (field.getType() != int[].class) { + throw new IllegalArgumentException("Field " + field.getName() + + " is not an integer array"); + } + + try { + int[] array = (int[]) field.get(null); + for (int i = 0; i < array.length; i++) { + array[i] = (array[i] & 0x00ffffff) | (packageId << 24); + } + } catch (IllegalAccessException e) { + // This should not occur (we check above if we can write to it) + throw new IllegalArgumentException(e); + } + } + + private void rewriteRValues(ClassLoader cl, String packageName, int id) { + try { + final Class<?> rClazz = cl.loadClass(packageName + ".R"); + Class<?>[] declaredClasses = rClazz.getDeclaredClasses(); + for (Class<?> clazz : declaredClasses) { + try { + if (clazz.getSimpleName().equals("styleable")) { + for (Field field : clazz.getDeclaredFields()) { + if (field.getType() == int[].class) { + rewriteIntArrayField(field, id); + } + } + + } else { + for (Field field : clazz.getDeclaredFields()) { + rewriteIntField(field, id); + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Failed to rewrite R values for " + + clazz.getName(), e); + } + } + + } catch (Exception e) { + throw new IllegalArgumentException("Failed to rewrite R values", e); + } + } + public void removeContextRegistrations(Context context, String who, String what) { final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled(); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 728f372..a67faa0 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -144,14 +144,16 @@ public class ResourcesManager { * Creates the top level Resources for applications with the given compatibility info. * * @param resDir the resource directory. + * @param overlayDirs the resource overlay directories. + * @param libDirs the shared library resource dirs this app references. * @param compatInfo the compability info. Must not be null. * @param token the application token for determining stack bounds. */ - public Resources getTopLevelResources(String resDir, String[] overlayDirs, int displayId, - Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { + public Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs, + int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo, + IBinder token) { final float scale = compatInfo.applicationScale; - ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, - token); + ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token); Resources r; synchronized (this) { // Resources is app scale dependent. @@ -186,6 +188,15 @@ public class ResourcesManager { } } + if (libDirs != null) { + for (String libDir : libDirs) { + if (assets.addAssetPath(libDir) == 0) { + Slog.w(TAG, "Asset path '" + libDir + + "' does not exist or contains no resources."); + } + } + } + //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); DisplayMetrics dm = getDisplayMetricsLocked(displayId); Configuration config; diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 2f8dd53..0c04401 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,6 +18,7 @@ package android.content.res; import android.os.ParcelFileDescriptor; import android.util.Log; +import android.util.SparseArray; import android.util.TypedValue; import java.io.FileNotFoundException; @@ -266,11 +267,9 @@ public final class AssetManager { } } - /*package*/ final CharSequence getPooledString(int block, int id) { - //System.out.println("Get pooled: block=" + block - // + ", id=#" + Integer.toHexString(id) - // + ", blocks=" + mStringBlocks); - return mStringBlocks[block-1].get(id); + /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) { + // Cookies map to string blocks starting at 1. + return mStringBlocks[cookie - 1].get(id); } /** @@ -740,6 +739,11 @@ public final class AssetManager { /** * {@hide} */ + public native final SparseArray<String> getAssignedPackageIdentifiers(); + + /** + * {@hide} + */ public native static final int getGlobalAssetCount(); /** diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index baf887e..d7199ff 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -834,7 +834,7 @@ public class TypedArray { } return null; } - return mAssets.getPooledString(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) { diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index 3ad357f..2f4d69b 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -375,7 +375,7 @@ final class XmlBlock { boolean defaultValue) { int t = nativeGetAttributeDataType(mParseState, idx); // Note: don't attempt to convert any other types, because - // we want to count on appt doing the conversion for us. + // we want to count on aapt doing the conversion for us. if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) { return nativeGetAttributeData(mParseState, idx) != 0; @@ -385,7 +385,7 @@ final class XmlBlock { public int getAttributeResourceValue(int idx, int defaultValue) { int t = nativeGetAttributeDataType(mParseState, idx); // Note: don't attempt to convert any other types, because - // we want to count on appt doing the conversion for us. + // we want to count on aapt doing the conversion for us. if (t == TypedValue.TYPE_REFERENCE) { return nativeGetAttributeData(mParseState, idx); } @@ -394,7 +394,7 @@ final class XmlBlock { public int getAttributeIntValue(int idx, int defaultValue) { int t = nativeGetAttributeDataType(mParseState, idx); // Note: don't attempt to convert any other types, because - // we want to count on appt doing the conversion for us. + // we want to count on aapt doing the conversion for us. if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) { return nativeGetAttributeData(mParseState, idx); @@ -404,7 +404,7 @@ final class XmlBlock { public int getAttributeUnsignedIntValue(int idx, int defaultValue) { int t = nativeGetAttributeDataType(mParseState, idx); // Note: don't attempt to convert any other types, because - // we want to count on appt doing the conversion for us. + // we want to count on aapt doing the conversion for us. if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) { return nativeGetAttributeData(mParseState, idx); @@ -414,7 +414,7 @@ final class XmlBlock { public float getAttributeFloatValue(int idx, float defaultValue) { int t = nativeGetAttributeDataType(mParseState, idx); // Note: don't attempt to convert any other types, because - // we want to count on appt doing the conversion for us. + // we want to count on aapt doing the conversion for us. if (t == TypedValue.TYPE_FLOAT) { return Float.intBitsToFloat( nativeGetAttributeData(mParseState, idx)); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 9dde701..4147608 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -73,6 +73,13 @@ static struct assetmanager_offsets_t jfieldID mObject; } gAssetManagerOffsets; +static struct sparsearray_offsets_t +{ + jclass classObject; + jmethodID constructor; + jmethodID put; +} gSparseArrayOffsets; + jclass g_stringClass = NULL; // ---------------------------------------------------------------------------- @@ -905,6 +912,26 @@ static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject c return str; } +static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const ResTable& res = am->getResources(); + + jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, + gSparseArrayOffsets.constructor); + const size_t N = res.getBasePackageCount(); + for (size_t i = 0; i < N; i++) { + const String16 name = res.getBasePackageName(i); + env->CallVoidMethod(sparseArray, gSparseArrayOffsets.put, (jint) res.getBasePackageId(i), + env->NewString(name, name.size())); + } + return sparseArray; +} + static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) { AssetManager* am = assetManagerForJavaObject(env, clazz); @@ -1675,16 +1702,19 @@ static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobjec return 0; } - Asset* a = cookie - ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER); + 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; } - ResXMLTree* block = new ResXMLTree(); + 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; @@ -1972,6 +2002,8 @@ static JNINativeMethod gAssetManagerMethods[] = { (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", @@ -2068,6 +2100,16 @@ int register_android_content_AssetManager(JNIEnv* env) g_stringClass = (jclass)env->NewGlobalRef(stringClass); LOG_FATAL_IF(g_stringClass == NULL, "Unable to create global reference for class java/lang/String"); + jclass sparseArrayClass = env->FindClass("android/util/SparseArray"); + LOG_FATAL_IF(sparseArrayClass == NULL, "Unable to find class android/util/SparseArray"); + gSparseArrayOffsets.classObject = (jclass) env->NewGlobalRef(sparseArrayClass); + gSparseArrayOffsets.constructor = + env->GetMethodID(gSparseArrayOffsets.classObject, "<init>", "()V"); + LOG_FATAL_IF(gSparseArrayOffsets.constructor == NULL, "Unable to find SparseArray.<init>()"); + gSparseArrayOffsets.put = + env->GetMethodID(gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + LOG_FATAL_IF(gSparseArrayOffsets.put == NULL, "Unable to find SparseArray.put(int, V)"); + return AndroidRuntime::registerNativeMethods(env, "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods)); } diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp index 03de5c0..2cccb83 100644 --- a/core/jni/android_util_XmlBlock.cpp +++ b/core/jni/android_util_XmlBlock.cpp @@ -47,10 +47,11 @@ static jlong android_content_XmlBlock_nativeCreate(JNIEnv* env, jobject clazz, } jbyte* b = env->GetByteArrayElements(bArray, NULL); - ResXMLTree* osb = new ResXMLTree(b+off, len, true); + ResXMLTree* osb = new ResXMLTree(); + osb->setTo(b+off, len, true); env->ReleaseByteArrayElements(bArray, b, 0); - if (osb == NULL || osb->getError() != NO_ERROR) { + if (osb->getError() != NO_ERROR) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return 0; } @@ -113,6 +114,8 @@ static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz, return 1; case ResXMLParser::BAD_DOCUMENT: goto bad; + default: + break; } } while (true); diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h index a13dd16..610528c 100644 --- a/include/androidfw/AssetManager.h +++ b/include/androidfw/AssetManager.h @@ -161,7 +161,7 @@ public: * path hierarchy, and will not be seen by "AssetDir" or included * in our filename cache. */ - Asset* openNonAsset(const char* fileName, AccessMode mode); + Asset* openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie = NULL); /* * Explicit non-asset file. The file explicitly named by the cookie (the diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index b334aab..7cc10be 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -25,6 +25,7 @@ #include <utils/Errors.h> #include <utils/String16.h> #include <utils/Vector.h> +#include <utils/KeyedVector.h> #include <utils/threads.h> @@ -216,7 +217,8 @@ enum { // Chunk types in RES_TABLE_TYPE RES_TABLE_PACKAGE_TYPE = 0x0200, RES_TABLE_TYPE_TYPE = 0x0201, - RES_TABLE_TYPE_SPEC_TYPE = 0x0202 + RES_TABLE_TYPE_SPEC_TYPE = 0x0202, + RES_TABLE_LIBRARY_TYPE = 0x0203 }; /** @@ -268,6 +270,9 @@ struct Res_value // The 'data' holds a complex number encoding a fraction of a // container. TYPE_FRACTION = 0x06, + // The 'data' holds a dynamic ResTable_ref, which needs to be + // resolved before it can be used like a TYPE_REFERENCE. + TYPE_DYNAMIC_REFERENCE = 0x07, // Beginning of integer flavors... TYPE_FIRST_INT = 0x10, @@ -457,6 +462,7 @@ public: ResStringPool(const void* data, size_t size, bool copyData=false); ~ResStringPool(); + void setToEmpty(); status_t setTo(const void* data, size_t size, bool copyData=false); status_t getError() const; @@ -735,14 +741,16 @@ private: const void* mCurExt; }; +class DynamicRefTable; + /** * Convenience class for accessing data in a ResXMLTree resource. */ class ResXMLTree : public ResXMLParser { public: + ResXMLTree(const DynamicRefTable* dynamicRefTable); ResXMLTree(); - ResXMLTree(const void* data, size_t size, bool copyData=false); ~ResXMLTree(); status_t setTo(const void* data, size_t size, bool copyData=false); @@ -756,6 +764,8 @@ private: status_t validateNode(const ResXMLTree_node* node) const; + const DynamicRefTable* const mDynamicRefTable; + status_t mError; void* mOwnedData; const ResXMLTree_header* mHeader; @@ -1290,6 +1300,7 @@ struct ResTable_entry struct ResTable_map_entry : public ResTable_entry { // Resource identifier of the parent mapping, or 0 if there is none. + // This is always treated as a TYPE_DYNAMIC_REFERENCE. ResTable_ref parent; // Number of name/value pairs that follow for FLAG_COMPLEX. uint32_t count; @@ -1385,6 +1396,68 @@ struct ResTable_map }; /** + * A package-id to package name mapping for any shared libraries used + * in this resource table. The package-id's encoded in this resource + * table may be different than the id's assigned at runtime. We must + * be able to translate the package-id's based on the package name. + */ +struct ResTable_lib_header +{ + struct ResChunk_header header; + + // The number of shared libraries linked in this resource table. + uint32_t count; +}; + +/** + * A shared library package-id to package name entry. + */ +struct ResTable_lib_entry +{ + // The package-id this shared library was assigned at build time. + // We use a uint32 to keep the structure aligned on a uint32 boundary. + uint32_t packageId; + + // The package name of the shared library. \0 terminated. + char16_t packageName[128]; +}; + +/** + * Holds the shared library ID table. Shared libraries are assigned package IDs at + * build time, but they may be loaded in a different order, so we need to maintain + * a mapping of build-time package ID to run-time assigned package ID. + * + * Dynamic references are not currently supported in overlays. Only the base package + * may have dynamic references. + */ +class DynamicRefTable +{ +public: + DynamicRefTable(uint8_t packageId); + + // Loads an unmapped reference table from the package. + status_t load(const ResTable_lib_header* const header); + + // Creates a mapping from build-time package ID to run-time package ID for + // the given package. + status_t addMapping(const String16& packageName, uint8_t packageId); + + // Performs the actual conversion of build-time resource ID to run-time + // resource ID. + inline status_t lookupResourceId(uint32_t* resId) const; + inline status_t lookupResourceValue(Res_value* value) const; + + inline const KeyedVector<String16, uint8_t>& entries() const { + return mEntries; + } + +private: + const uint8_t mAssignedPackageId; + uint8_t mLookupTable[256]; + KeyedVector<String16, uint8_t> mEntries; +}; + +/** * Convenience class for accessing data in a ResTable resource. */ class ResTable @@ -1399,6 +1472,7 @@ public: const void* idmap = NULL); status_t add(const void *data, size_t size); status_t add(ResTable* src); + status_t addEmpty(const int32_t cookie); status_t getError() const; @@ -1634,7 +1708,7 @@ public: bool append = false); size_t getBasePackageCount() const; - const char16_t* getBasePackageName(size_t idx) const; + const String16 getBasePackageName(size_t idx) const; uint32_t getBasePackageId(size_t idx) const; // Return the number of resource tables that the object contains. @@ -1647,6 +1721,8 @@ public: // Return unique cookie identifier for the given resource table. int32_t getTableCookie(size_t index) const; + const DynamicRefTable* getDynamicRefTableForCookie(int32_t cookie) const; + // Return the configurations (ResTable_config) that we know about void getConfigurations(Vector<ResTable_config>* configs) const; @@ -1684,7 +1760,7 @@ private: struct bag_set; status_t addInternal(const void* data, size_t size, const int32_t cookie, - Asset* asset, bool copyData, const Asset* idmap); + bool copyData, const Asset* idmap); ssize_t getResourcePackageIndex(uint32_t resID) const; ssize_t getEntry( @@ -1712,6 +1788,8 @@ private: // Mapping from resource package IDs to indices into the internal // package array. uint8_t mPackageMap[256]; + + uint8_t mNextPackageId; }; } // namespace android diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 64363d4..91dda75 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -74,6 +74,7 @@ static const char* kAssetsRoot = "assets"; static const char* kAppZipName = NULL; //"classes.jar"; static const char* kSystemAssets = "framework/framework-res.apk"; static const char* kResourceCache = "resource-cache"; +static const char* kAndroidManifest = "AndroidManifest.xml"; static const char* kExcludeExtension = ".EXCLUDE"; @@ -205,6 +206,16 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie) ALOGV("In %p Asset %s path: %s", this, ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); + // Check that the path has an AndroidManifest.xml + Asset* manifestAsset = const_cast<AssetManager*>(this)->openNonAssetInPathLocked( + kAndroidManifest, Asset::ACCESS_BUFFER, ap); + if (manifestAsset == NULL) { + // This asset path does not contain any resources. + delete manifestAsset; + return false; + } + delete manifestAsset; + mAssetPaths.add(ap); // new paths are always added at the end @@ -461,7 +472,7 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode) * The "fileName" is the partial path starting from the application * name. */ -Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) +Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie) { AutoMutex _l(mLock); @@ -482,6 +493,7 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) Asset* pAsset = openNonAssetInPathLocked( fileName, mode, mAssetPaths.itemAt(i)); if (pAsset != NULL) { + if (outCookie != NULL) *outCookie = static_cast<int32_t>(i + 1); return pAsset != kExcludedAsset ? pAsset : NULL; } } @@ -556,9 +568,14 @@ const ResTable* AssetManager::getResTable(bool required) const LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); } - if (mCacheMode != CACHE_OFF && !mCacheValid) + if (mCacheMode != CACHE_OFF && !mCacheValid) { const_cast<AssetManager*>(this)->loadFileNameCacheLocked(); + } + + mResources = new ResTable(); + updateResourceParamsLocked(); + bool onlyEmptyResources = true; const size_t N = mAssetPaths.size(); for (size_t i=0; i<N; i++) { Asset* ass = NULL; @@ -621,35 +638,39 @@ const ResTable* AssetManager::getResTable(bool required) const ap); shared = false; } + if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { - if (rt == NULL) { - mResources = rt = new ResTable(); - updateResourceParamsLocked(); - } ALOGV("Installing resource asset %p in to table %p\n", ass, mResources); if (sharedRes != NULL) { ALOGV("Copying existing resources for %s", ap.path.string()); - rt->add(sharedRes); + mResources->add(sharedRes); } else { ALOGV("Parsing resources for %s", ap.path.string()); - rt->add(ass, i + 1, !shared, idmap); + mResources->add(ass, i + 1, !shared, idmap); } + onlyEmptyResources = false; if (!shared) { delete ass; } + } else { + ALOGW("Installing empty resources in to table %p\n", mResources); + mResources->addEmpty(i + 1); } + if (idmap != NULL) { delete idmap; } MY_TRACE_END(); } - if (required && !rt) ALOGW("Unable to find resources file resources.arsc"); - if (!rt) { - mResources = rt = new ResTable(); + if (required && onlyEmptyResources) { + ALOGW("Unable to find resources file resources.arsc"); + delete mResources; + mResources = NULL; } - return rt; + + return mResources; } void AssetManager::updateResourceParamsLocked() const diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 652cd4a..04ca81e 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -42,6 +42,7 @@ #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x #define TABLE_THEME(x) //x +#define LIB_NOISY(x) x namespace android { @@ -66,6 +67,9 @@ namespace android { // size measured in sizeof(uint32_t) #define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) +#define APP_PACKAGE_ID 0x7f +#define SYS_PACKAGE_ID 0x01 + // Standard C isspace() is only required to look at the low byte of its input, so // produces incorrect results for UTF-16 characters. For safety's sake, assume that // any high-byte UTF-16 code point is not whitespace. @@ -113,7 +117,7 @@ static status_t validate_chunk(const ResChunk_header* chunk, name, size, headerSize); return BAD_TYPE; } - ALOGW("%s header size 0x%x is too small.", + ALOGW("%s header size 0x%04x is too small.", name, headerSize); return BAD_TYPE; } @@ -264,7 +268,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } const uint32_t index = typeOffset + 2 + entry - entryOffset; if (index > size) { - ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size); + ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size); *outValue = 0; return NO_ERROR; } @@ -279,7 +283,7 @@ static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t return UNKNOWN_ERROR; } if (mapSize <= IDMAP_HEADER_SIZE + 1) { - ALOGW("corrupt idmap: map size %d too short\n", mapSize); + ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize); return UNKNOWN_ERROR; } uint32_t typeCount = *(map + IDMAP_HEADER_SIZE); @@ -288,7 +292,7 @@ static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t return UNKNOWN_ERROR; } if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) { - ALOGW("corrupt idmap: number of types %d extends past idmap size %d\n", typeCount, mapSize); + ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize); return UNKNOWN_ERROR; } const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; @@ -304,7 +308,7 @@ static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t // determine package id from first entry of first type const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2; if (offset > mapSize) { - ALOGW("corrupt idmap: entry offset %d points outside map size %d\n", offset, mapSize); + ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize); return UNKNOWN_ERROR; } *outId = (map[offset] >> 24) & 0x000000ff; @@ -342,6 +346,22 @@ ResStringPool::~ResStringPool() uninit(); } +void ResStringPool::setToEmpty() +{ + uninit(); + + mOwnedData = calloc(1, sizeof(ResStringPool_header)); + ResStringPool_header* header = (ResStringPool_header*) mOwnedData; + mSize = 0; + mEntries = NULL; + mStrings = NULL; + mStringPoolSize = 0; + mEntryStyles = NULL; + mStyles = NULL; + mStylePoolSize = 0; + mHeader = (const ResStringPool_header*) header; +} + status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) { if (!data || !size) { @@ -1111,7 +1131,14 @@ int32_t ResXMLParser::getAttributeDataType(size_t idx) const (((const uint8_t*)tag) + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); - return attr->typedValue.dataType; + uint8_t type = attr->typedValue.dataType; + if (type != Res_value::TYPE_DYNAMIC_REFERENCE) { + return type; + } + + // This is a dynamic reference. We adjust those references + // to regular references at this level, so lie to the caller. + return Res_value::TYPE_REFERENCE; } } return Res_value::TYPE_NULL; @@ -1126,7 +1153,15 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const (((const uint8_t*)tag) + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->typedValue.data); + if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE || + mTree.mDynamicRefTable == NULL) { + return dtohl(attr->typedValue.data); + } + + uint32_t data = dtohl(attr->typedValue.data); + if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) { + return data; + } } } return 0; @@ -1142,6 +1177,10 @@ ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); outValue->copyFrom_dtoh(attr->typedValue); + if (mTree.mDynamicRefTable != NULL && + mTree.mDynamicRefTable->lookupResourceValue(outValue) != NO_ERROR) { + return BAD_TYPE; + } return sizeof(Res_value); } } @@ -1333,25 +1372,26 @@ void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) mCurExt = pos.curExt; } - // -------------------------------------------------------------------- static volatile int32_t gCount = 0; -ResXMLTree::ResXMLTree() +ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) : ResXMLParser(*this) + , mDynamicRefTable(dynamicRefTable) , mError(NO_INIT), mOwnedData(NULL) { //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); restart(); } -ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) +ResXMLTree::ResXMLTree() : ResXMLParser(*this) + , mDynamicRefTable(NULL) , mError(NO_INIT), mOwnedData(NULL) { //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - setTo(data, size, copyData); + restart(); } ResXMLTree::~ResXMLTree() @@ -2737,7 +2777,14 @@ struct ResTable::Package struct ResTable::PackageGroup { PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id) - : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { } + : owner(_owner) + , name(_name) + , id(_id) + , typeCount(0) + , bags(NULL) + , dynamicRefTable(static_cast<uint8_t>(_id)) + { } + ~PackageGroup() { clearBagCache(); const size_t N = packages.size(); @@ -2790,6 +2837,13 @@ struct ResTable::PackageGroup // Computed attribute bags, first indexed by the type and second // by the entry in that type. bag_set*** bags; + + // The table mapping dynamic references to resolved references for + // this package group. + // TODO: We may be able to support dynamic references in overlays + // by having these tables in a per-package scope rather than + // per-package-group. + DynamicRefTable dynamicRefTable; }; struct ResTable::bag_set @@ -3077,7 +3131,7 @@ void ResTable::Theme::dumpToLog() const } ResTable::ResTable() - : mError(NO_INIT) + : mError(NO_INIT), mNextPackageId(2) { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); @@ -3085,11 +3139,11 @@ ResTable::ResTable() } ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData) - : mError(NO_INIT) + : mError(NO_INIT), mNextPackageId(2) { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); - addInternal(data, size, cookie, NULL /* asset */, copyData, NULL /* idMap */); + addInternal(data, size, cookie, copyData, NULL /* idMap */); LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); //ALOGI("Creating ResTable %p\n", this); } @@ -3106,7 +3160,7 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const } status_t ResTable::add(const void* data, size_t size) { - return addInternal(data, size, 0 /* cookie */, NULL /* asset */, + return addInternal(data, size, 0 /* cookie */, false /* copyData */, NULL /* idMap */); } @@ -3118,7 +3172,7 @@ status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const return UNKNOWN_ERROR; } size_t size = (size_t)asset->getLength(); - return addInternal(data, size, cookie, asset, copyData, + return addInternal(data, size, cookie, copyData, reinterpret_cast<const Asset*>(idmap)); } @@ -3146,8 +3200,25 @@ status_t ResTable::add(ResTable* src) return mError; } +status_t ResTable::addEmpty(const int32_t cookie) { + Header* header = new Header(this); + header->index = mHeaders.size(); + header->cookie = cookie; + header->values.setToEmpty(); + header->ownedData = calloc(1, sizeof(ResTable_header)); + + ResTable_header* resHeader = (ResTable_header*) header->ownedData; + resHeader->header.type = RES_TABLE_TYPE; + resHeader->header.headerSize = sizeof(ResTable_header); + resHeader->header.size = sizeof(ResTable_header); + + header->header = (const ResTable_header*) resHeader; + mHeaders.add(header); + return NO_ERROR; +} + status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie, - Asset* /*asset*/, bool copyData, const Asset* idmap) + bool copyData, const Asset* idmap) { if (!data) return NO_ERROR; Header* header = new Header(this); @@ -3186,8 +3257,6 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, // dtohl(header->header->header.size), header->header->header.size); LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); - LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, - 16, 16, 0, false, printToLogFunc)); if (dtohs(header->header->header.headerSize) > header->size || header->size > size) { ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", @@ -3474,9 +3543,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag continue; } - TABLE_NOISY(aout << "Resource type data: " - << HexDump(type, dtohl(type->header.size)) << endl); - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { ALOGW("ResTable_item at %d is beyond type chunk data %d", (int)offset, dtohl(type->header.size)); @@ -3516,6 +3582,18 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->res0 = bestValue->res0; outValue->dataType = bestValue->dataType; outValue->data = dtohl(bestValue->data); + + // The reference may be pointing to a resource in a shared library. These + // references have build-time generated package IDs. These ids may not match + // the actual package IDs of the corresponding packages in this ResTable. + // We need to fix the package ID based on a mapping. + status_t err = grp->dynamicRefTable.lookupResourceValue(outValue); + if (err != NO_ERROR) { + ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); + rc = BAD_VALUE; + goto out; + } + if (outConfig != NULL) { *outConfig = bestItem; } @@ -3545,8 +3623,8 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, ResTable_config* outConfig) const { int count=0; - while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE - && value->data != 0 && count < 20) { + while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE + && value->data != 0 && count < 20) { if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; @@ -3730,7 +3808,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, const Type* typeClass; ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", offset); + ALOGV("Resulting offset=%d\n", (int)offset); if (offset <= 0) { // No {entry, appropriate config} pair found in package. If this // package is an overlay package (ip != 0), this simply means the @@ -3774,9 +3852,21 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent)); if (parent) { + uint32_t resolvedParent = parent; + + // Bags encode a parent reference without using the standard + // Res_value structure. That means we must always try to + // resolve a parent reference in case it is actually a + // TYPE_DYNAMIC_REFERENCE. + status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + ALOGE("Failed resolving bag parent id 0x%08x", parent); + return UNKNOWN_ERROR; + } + const bag_entry* parentBag; uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); const size_t NT = ((NP >= 0) ? NP : 0) + N; set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); if (set == NULL) { @@ -3871,6 +3961,12 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, cur->stringBlock = package->header->index; cur->map.name.ident = newName; cur->map.value.copyFrom_dtoh(map->value); + status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); + if (err != NO_ERROR) { + ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); + return UNKNOWN_ERROR; + } + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", curEntry, cur, cur->stringBlock, cur->map.name.ident, cur->map.value.dataType, cur->map.value.data)); @@ -4568,16 +4664,20 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, return false; } } - if (!accessor) { - outValue->data = rid; - return true; + + if (accessor) { + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + } + + uint32_t packageId = Res_GETPACKAGE(rid) + 1; + if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) { + outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); outValue->data = rid; return true; } @@ -4589,8 +4689,17 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", String8(package).string(), String8(type).string(), String8(name).string(), rid)); - outValue->data = rid; - return true; + uint32_t packageId = Res_GETPACKAGE(rid) + 1; + if (packageId == 0x00) { + outValue->data = rid; + outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; + return true; + } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) { + // We accept packageId's generated as 0x01 in order to support + // building the android system resources + outValue->data = rid; + return true; + } } } } @@ -5122,15 +5231,15 @@ size_t ResTable::getBasePackageCount() const return mPackageGroups.size(); } -const char16_t* ResTable::getBasePackageName(size_t idx) const +const String16 ResTable::getBasePackageName(size_t idx) const { if (mError != NO_ERROR) { - return 0; + return String16(); } LOG_FATAL_IF(idx >= mPackageGroups.size(), "Requested package index %d past package count %d", (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->name.string(); + return mPackageGroups[idx]->name; } uint32_t ResTable::getBasePackageId(size_t idx) const @@ -5159,6 +5268,21 @@ int32_t ResTable::getTableCookie(size_t index) const return mHeaders[index]->cookie; } +const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) const +{ + const size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + const PackageGroup* pg = mPackageGroups[i]; + size_t M = pg->packages.size(); + for (size_t j = 0; j < M; j++) { + if (pg->packages[j]->header->cookie == cookie) { + return &pg->dynamicRefTable; + } + } + } + return NULL; +} + void ResTable::getConfigurations(Vector<ResTable_config>* configs) const { const size_t I = mPackageGroups.size(); @@ -5300,10 +5424,9 @@ ssize_t ResTable::getEntry( } offset += dtohl(type->entriesStart); - TABLE_NOISY(aout << "Looking in resource table " << package->header->header - << ", typeOff=" - << (void*)(((const char*)type)-((const char*)package->header->header)) - << ", offset=" << (void*)offset << endl); + TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p", + package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)), + (void*)offset)); if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", @@ -5377,6 +5500,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (package == NULL) { return (mError=NO_MEMORY); } + + if (id == 0) { + // This is a library so assign an ID + id = mNextPackageId++; + } size_t idx = mPackageMap[id]; if (idx == 0) { @@ -5413,6 +5541,13 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, group->basePackage = package; mPackageMap[id] = (uint8_t)idx; + + // Find all packages that reference this package + size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + mPackageGroups[i]->dynamicRefTable.addMapping( + group->name, static_cast<uint8_t>(group->id)); + } } else { group = mPackageGroups.itemAt(idx-1); if (group == NULL) { @@ -5457,7 +5592,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (void*)(base-(const uint8_t*)chunk), dtohs(typeSpec->header.type), dtohs(typeSpec->header.headerSize), - (void*)typeSize)); + (void*)typeSpecSize)); // look for block overrun or int overflow when multiplying by 4 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) @@ -5543,6 +5678,21 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, ALOGI("Adding config to type %d: %s\n", type->id, thisConfig.toString().string())); t->configs.add(type); + } else if (ctype == RES_TABLE_LIBRARY_TYPE) { + if (group->dynamicRefTable.entries().size() == 0) { + status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk); + if (err != NO_ERROR) { + return (mError=err); + } + + // Fill in the reference table with the entries we already know about. + size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id); + } + } else { + ALOGW("Found multiple library tables, ignoring..."); + } } else { status_t err = validate_chunk(chunk, sizeof(ResChunk_header), endPos, "ResTable_package:unknown"); @@ -5561,6 +5711,103 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return NO_ERROR; } +DynamicRefTable::DynamicRefTable(uint8_t packageId) + : mAssignedPackageId(packageId) +{ + memset(mLookupTable, 0, sizeof(mLookupTable)); + + // Reserved package ids + mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID; + mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; +} + +status_t DynamicRefTable::load(const ResTable_lib_header* const header) +{ + const uint32_t entryCount = dtohl(header->count); + const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount; + const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize); + if (sizeOfEntries > expectedSize) { + ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).", + expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry)); + return UNKNOWN_ERROR; + } + + const ResTable_lib_entry* entry = (const ResTable_lib_entry*)(((uint8_t*) header) + + dtohl(header->header.headerSize)); + for (uint32_t entryIndex = 0; entryIndex < entryCount; entryIndex++) { + uint32_t packageId = dtohl(entry->packageId); + char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)]; + strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t)); + LIB_NOISY(ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(), + dtohl(entry->packageId))); + if (packageId >= 256) { + ALOGE("Bad package id 0x%08x", packageId); + return UNKNOWN_ERROR; + } + mEntries.replaceValueFor(String16(tmpName), (uint8_t) packageId); + entry = entry + 1; + } + return NO_ERROR; +} + +status_t DynamicRefTable::addMapping(const String16& packageName, uint8_t packageId) +{ + ssize_t index = mEntries.indexOfKey(packageName); + if (index < 0) { + return UNKNOWN_ERROR; + } + mLookupTable[mEntries.valueAt(index)] = packageId; + return NO_ERROR; +} + +status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const { + uint32_t res = *resId; + size_t packageId = Res_GETPACKAGE(res) + 1; + + if (packageId == APP_PACKAGE_ID) { + // No lookup needs to be done, app package IDs are absolute. + return NO_ERROR; + } + + if (packageId == 0) { + // The package ID is 0x00. That means that a shared library is accessing + // its own local resource, so we fix up the resource with the calling + // package ID. + *resId |= ((uint32_t) mAssignedPackageId) << 24; + return NO_ERROR; + } + + // Do a proper lookup. + uint8_t translatedId = mLookupTable[packageId]; + if (translatedId == 0) { + ALOGV("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.", + (uint8_t)mAssignedPackageId, (uint8_t)packageId); + for (size_t i = 0; i < 256; i++) { + if (mLookupTable[i] != 0) { + ALOGV("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]); + } + } + return UNKNOWN_ERROR; + } + + *resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24); + return NO_ERROR; +} + +status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { + if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) { + return NO_ERROR; + } + + status_t err = lookupResourceId(&value->data); + if (err != NO_ERROR) { + return err; + } + + value->dataType = Res_value::TYPE_REFERENCE; + return NO_ERROR; +} + status_t ResTable::createIdmap(const ResTable& overlay, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, @@ -5701,7 +5948,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, continue; } if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) - ALOGW("idmap: type %d supposedly has entries, but no entries found\n", i); + ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i); return UNKNOWN_ERROR; } *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) @@ -5815,6 +6062,8 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const printf("(null)\n"); } else if (value.dataType == Res_value::TYPE_REFERENCE) { printf("(reference) 0x%08x\n", value.data); + } else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) { + printf("(dynamic reference) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) { printf("(attribute) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_STRING) { @@ -5897,6 +6146,11 @@ void ResTable::print(bool inclValues) const uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); + // Since we are creating resID without actually + // iterating over them, we have no idea which is a + // dynamic reference. We must check. + pg->dynamicRefTable.lookupResourceId(&resID); + resource_name resName; if (this->getResourceName(resID, true, &resName)) { String8 type8; @@ -5956,6 +6210,7 @@ void ResTable::print(bool inclValues) const uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); + pg->dynamicRefTable.lookupResourceId(&resID); resource_name resName; if (this->getResourceName(resID, true, &resName)) { String8 type8; @@ -6034,8 +6289,14 @@ void ResTable::print(bool inclValues) const const uint8_t* baseMapPtr = (const uint8_t*)ent; size_t mapOffset = esize; const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - printf(" Parent=0x%08x, Count=%d\n", - dtohl(bagPtr->parent.ident), N); + const uint32_t parent = dtohl(bagPtr->parent.ident); + uint32_t resolvedParent = parent; + status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + resolvedParent = 0; + } + printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", + parent, resolvedParent, N); for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { printf(" #%i (Key=0x%08x): ", i, dtohl(mapPtr->name.ident)); diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp index 2c9f650..7a4dd13 100644 --- a/libs/androidfw/tests/ObbFile_test.cpp +++ b/libs/androidfw/tests/ObbFile_test.cpp @@ -91,7 +91,7 @@ TEST_F(ObbFileTest, WriteThenRead) { EXPECT_EQ(sizeof(salt), saltLen) << "salt sizes were not the same"; - for (int i = 0; i < sizeof(salt); i++) { + for (size_t i = 0; i < sizeof(salt); i++) { EXPECT_EQ(salt[i], newSalt[i]) << "salt character " << i << " should be equal"; } diff --git a/tests/SharedLibrary/client/AndroidManifest.xml b/tests/SharedLibrary/client/AndroidManifest.xml index c6a43c0..a39c754 100644 --- a/tests/SharedLibrary/client/AndroidManifest.xml +++ b/tests/SharedLibrary/client/AndroidManifest.xml @@ -16,7 +16,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.test.lib_client"> - <application android:label="@string/app_title"> + <application android:label="@string/app_title" android:theme="@style/Theme"> <uses-library android:name="android.test.runner" /> <uses-library android:name="com.google.android.test.shared_library" /> <activity android:name="ActivityMain"> diff --git a/tests/SharedLibrary/client/res/layout/main.xml b/tests/SharedLibrary/client/res/layout/main.xml new file mode 100644 index 0000000..067ef9f --- /dev/null +++ b/tests/SharedLibrary/client/res/layout/main.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView android:id="@+id/label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@com.google.android.test.shared_library:string/shared_string" + style="@com.google.android.test.shared_library:style/CodeFont"/> + + <com.google.android.test.shared_library.AddressView + xmlns:custom="http://schemas.android.com/apk/res/com.google.android.test.shared_library" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + custom:name="Professor Android" + custom:streetNumber="44" + custom:streetName="KitKat Lane" + custom:city="AndroidVille" + custom:state="OS" + custom:country="Mobile" + custom:zip="12345"/> +</LinearLayout> diff --git a/tests/SharedLibrary/client/res/values/strings.xml b/tests/SharedLibrary/client/res/values/strings.xml index 3757a25..d9efdc7 100644 --- a/tests/SharedLibrary/client/res/values/strings.xml +++ b/tests/SharedLibrary/client/res/values/strings.xml @@ -16,4 +16,5 @@ <resources> <string name="app_title">SharedLibrary client</string> + <string name="changes">@com.google.android.test.shared_library:string/shared_string</string> </resources> diff --git a/tests/SharedLibrary/client/res/values/themes.xml b/tests/SharedLibrary/client/res/values/themes.xml new file mode 100644 index 0000000..a14f98a --- /dev/null +++ b/tests/SharedLibrary/client/res/values/themes.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <style name="Theme" parent="com.google.android.test.shared_library:Theme"> + </style> +</resources> diff --git a/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java index d6121a5..7276b3c 100644 --- a/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java +++ b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java @@ -18,18 +18,33 @@ package com.google.android.test.lib_client; import android.app.Activity; import android.os.Bundle; -import android.widget.TextView; import com.google.android.test.shared_library.SharedLibraryMain; public class ActivityMain extends Activity { + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + String[] expectedAnimals = new String[] { + "Racoon", + "Rhino", + "Elephant" + }; + + String[] animals = getResources().getStringArray(com.google.android.test.shared_library.R.array.animals); + if (animals == null || animals.length != expectedAnimals.length) { + throw new AssertionError("Animal list from shared library is null or wrong length."); + } - TextView content = new TextView(this); - content.setText("Library version: " + SharedLibraryMain.getVersion(this) + "!"); + for (int i = 0; i < expectedAnimals.length; i++) { + if (!expectedAnimals[i].equals(animals[i])) { + throw new AssertionError("Expected '" + expectedAnimals[i] + + "' at index " + i + " but got '" + animals[i]); + } + } SharedLibraryMain.ensureVersion(this, SharedLibraryMain.VERSION_BASE); - setContentView(content); } } diff --git a/tests/SharedLibrary/lib/Android.mk b/tests/SharedLibrary/lib/Android.mk index c19e23a..b2fc369 100644 --- a/tests/SharedLibrary/lib/Android.mk +++ b/tests/SharedLibrary/lib/Android.mk @@ -3,8 +3,13 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_AAPT_FLAGS := --shared-lib LOCAL_PACKAGE_NAME := SharedLibrary -LOCAL_MODULE_TAGS := tests +LOCAL_EXPORT_PACKAGE_RESOURCES := true +LOCAL_PRIVILEGED_MODULE := true +LOCAL_MODULE_TAGS := optional + +LOCAL_PROGUARD_FLAG_FILES := proguard.proguard include $(BUILD_PACKAGE) diff --git a/tests/SharedLibrary/lib/proguard.proguard b/tests/SharedLibrary/lib/proguard.proguard new file mode 100644 index 0000000..e5dfbe1 --- /dev/null +++ b/tests/SharedLibrary/lib/proguard.proguard @@ -0,0 +1,7 @@ +-keepparameternames +-keepattributes Exceptions,InnerClasses,Signature,Deprecated, + SourceFile,LineNumberTable,*Annotation*,EnclosingMethod + +-keep public class * { + public protected *; +} diff --git a/tests/SharedLibrary/lib/res/layout/address.xml b/tests/SharedLibrary/lib/res/layout/address.xml new file mode 100644 index 0000000..835f43e --- /dev/null +++ b/tests/SharedLibrary/lib/res/layout/address.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <TextView android:id="@+id/name" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/street" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/cityStateZip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/country" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</merge> diff --git a/tests/SharedLibrary/lib/res/values/attrs.xml b/tests/SharedLibrary/lib/res/values/attrs.xml new file mode 100644 index 0000000..8cefe92 --- /dev/null +++ b/tests/SharedLibrary/lib/res/values/attrs.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <declare-styleable name="AddressView"> + <attr name="name" format="string" /> + <attr name="streetNumber" format="integer" /> + <attr name="streetName" format="string" /> + <attr name="city" format="string" /> + <attr name="state" format="string" /> + <attr name="zip" format="string" /> + <attr name="country" format="string" /> + </declare-styleable> +</resources> diff --git a/tests/SharedLibrary/lib/res/values/public.xml b/tests/SharedLibrary/lib/res/values/public.xml new file mode 100644 index 0000000..37b1ec9 --- /dev/null +++ b/tests/SharedLibrary/lib/res/values/public.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <public type="string" name="shared_string" id="0x00020003" /> + <public type="style" name="CodeFont" id="0x00040000" /> + <public type="style" name="Theme" id="0x00040001" /> + + <public type="attr" name="name" id="0x00010000" /> + <public type="attr" name="streetNumber" id="0x00010001" /> + <public type="attr" name="streetName" id="0x00010002" /> + <public type="attr" name="city" id="0x00010003" /> + <public type="attr" name="state" id="0x00010004" /> + <public type="attr" name="zip" id="0x00010005" /> + <public type="attr" name="country" id="0x00010006" /> + + <public type="array" name="animals" id="0x02050000" /> +</resources> diff --git a/tests/SharedLibrary/lib/res/values/strings.xml b/tests/SharedLibrary/lib/res/values/strings.xml index bbfb0b4..6827f93 100644 --- a/tests/SharedLibrary/lib/res/values/strings.xml +++ b/tests/SharedLibrary/lib/res/values/strings.xml @@ -19,4 +19,13 @@ <string name="upgrade_body"><xliff:g id="app">%1$s</xliff:g> requires a newer version of <xliff:g id="lib">%2$s</xliff:g> to run.</string> <string name="upgrade_button">Upgrade</string> + <string name="shared_string">Shared string!</string> + + <string-array name="animals"> + <item>@string/racoon</item> + <item>Rhino</item> + <item>Elephant</item> + </string-array> + + <string name="racoon">Racoon</string> </resources> diff --git a/tests/SharedLibrary/lib/res/values/themes.xml b/tests/SharedLibrary/lib/res/values/themes.xml new file mode 100644 index 0000000..f1081ac --- /dev/null +++ b/tests/SharedLibrary/lib/res/values/themes.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <style name="CodeFont" parent="@android:style/TextAppearance.Medium"> + <item name="android:textColor">#00FF00</item> + <item name="android:typeface">monospace</item> + </style> + + <style name="Theme" parent="android:Theme.Holo.Light"> + <item name="android:actionBarStyle">@style/ActionBar</item> + </style> + + <style name="ActionBar" parent="android:Widget.Holo.Light.ActionBar.Solid.Inverse"> + <item name="android:background">@color/orange</item> + </style> + + <color name="orange">#f0ad4e</color> +</resources> diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java new file mode 100644 index 0000000..dcaf68c --- /dev/null +++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java @@ -0,0 +1,44 @@ +package com.google.android.test.shared_library; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class AddressView extends LinearLayout { + private TextView mNameView; + private TextView mStreetView; + private TextView mCityStateZipView; + private TextView mCountryView; + + public AddressView(Context context, AttributeSet attrs) { + super(context, attrs); + setOrientation(VERTICAL); + + View view = LayoutInflater.from(context).inflate(R.layout.address, this); + mNameView = (TextView) view.findViewById(R.id.name); + mStreetView = (TextView) view.findViewById(R.id.street); + mCityStateZipView = (TextView) view.findViewById(R.id.cityStateZip); + mCountryView = (TextView) view.findViewById(R.id.country); + + TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.AddressView, + 0, 0); + try { + mNameView.setText(a.getString(R.styleable.AddressView_name)); + int streetNumber = a.getInteger(R.styleable.AddressView_streetNumber, -1); + mStreetView.setText((streetNumber <= 0 ? "" : Integer.toString(streetNumber)) + + " " + a.getString(R.styleable.AddressView_streetName)); + mCityStateZipView.setText(a.getString(R.styleable.AddressView_city) + ", " + + a.getString(R.styleable.AddressView_state) + " " + + a.getString(R.styleable.AddressView_zip)); + mCountryView.setText(a.getString(R.styleable.AddressView_country)); + } finally { + a.recycle(); + } + } +} diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index f9a2d19..38bfa00 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -1798,6 +1798,11 @@ void* AaptFile::editData(size_t size) return buf; } +void* AaptFile::editDataInRange(size_t offset, size_t size) +{ + return (void*)(((uint8_t*) editData(offset + size)) + offset); +} + void* AaptFile::editData(size_t* outSize) { if (outSize) { diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 336d08b..82dda5f 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -251,6 +251,7 @@ public: size_t getSize() const { return mDataSize; } void* editData(size_t size); void* editData(size_t* outSize = NULL); + void* editDataInRange(size_t offset, size_t size); void* padData(size_t wordSize); status_t writeData(const void* data, size_t size); void clearData(); diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 49b8b55..d2eccbe 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -64,6 +64,7 @@ public: mProduct(NULL), mUseCrunchCache(false), mErrorOnFailedInsert(false), mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL), mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL), + mBuildSharedLibrary(false), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -188,6 +189,8 @@ public: void setSingleCrunchInputFile(const char* val) { mSingleCrunchInputFile = val; } const char* getSingleCrunchOutputFile() const { return mSingleCrunchOutputFile; } void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; } + bool getBuildSharedLibrary() const { return mBuildSharedLibrary; } + void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; } /* * Set and get the file specification. @@ -300,6 +303,7 @@ private: const char* mOutputTextSymbols; const char* mSingleCrunchInputFile; const char* mSingleCrunchOutputFile; + bool mBuildSharedLibrary; /* file specification */ int mArgc; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 33711fa..1cf4783 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -190,6 +190,9 @@ void usage(void) " Make the resources ID non constant. This is required to make an R java class\n" " that does not contain the final value but is used to make reusable compiled\n" " libraries that need to access resources.\n" + " --shared-lib\n" + " Make a shared library resource package that can be loaded by an application\n" + " at runtime to access the libraries resources. Implies --non-constant-id.\n" " --error-on-failed-insert\n" " Forces aapt to return an error if it fails to insert values into the manifest\n" " with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n" @@ -618,6 +621,9 @@ int main(int argc, char* const argv[]) bundle.setProduct(argv[0]); } else if (strcmp(cp, "-non-constant-id") == 0) { bundle.setNonConstantId(true); + } else if (strcmp(cp, "-shared-lib") == 0) { + bundle.setNonConstantId(true); + bundle.setBuildSharedLibrary(true); } else if (strcmp(cp, "-no-crunch") == 0) { bundle.setUseCrunchCache(true); } else if (strcmp(cp, "-ignore-assets") == 0) { diff --git a/tools/aapt/ResourceIdCache.cpp b/tools/aapt/ResourceIdCache.cpp index e03f4f6..d60a07f 100644 --- a/tools/aapt/ResourceIdCache.cpp +++ b/tools/aapt/ResourceIdCache.cpp @@ -98,10 +98,10 @@ uint32_t ResourceIdCache::store(const android::String16& package, void ResourceIdCache::dump() { printf("ResourceIdCache dump:\n"); - printf("Size: %ld\n", mIdMap.size()); - printf("Hits: %ld\n", mHits); - printf("Misses: %ld\n", mMisses); - printf("(Collisions: %ld)\n", mCollisions); + printf("Size: %zd\n", mIdMap.size()); + printf("Hits: %zd\n", mHits); + printf("Misses: %zd\n", mMisses); + printf("(Collisions: %zd)\n", mCollisions); } } diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h index e6bcda2..3acdee1 100644 --- a/tools/aapt/ResourceIdCache.h +++ b/tools/aapt/ResourceIdCache.h @@ -6,18 +6,20 @@ #ifndef RESOURCE_ID_CACHE_H #define RESOURCE_ID_CACHE_H +#include <utils/String16.h> + namespace android { class ResourceIdCache { public: - static uint32_t lookup(const android::String16& package, - const android::String16& type, - const android::String16& name, + static uint32_t lookup(const String16& package, + const String16& type, + const String16& name, bool onlyPublic); - static uint32_t store(const android::String16& package, - const android::String16& type, - const android::String16& name, + static uint32_t store(const String16& package, + const String16& type, + const String16& name, bool onlyPublic, uint32_t resId); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 652998e..0fb2606 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -1673,7 +1673,7 @@ status_t compileResourceFile(Bundle* bundle, ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage) : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false), - mIsAppPackage(!bundle->getExtending()), + mIsAppPackage(!bundle->getExtending()), mIsSharedLibrary(bundle->getBuildSharedLibrary()), mNumLocal(0), mBundle(bundle) { @@ -1695,8 +1695,9 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets const size_t N = incl.getBasePackageCount(); for (size_t phase=0; phase<2; phase++) { for (size_t i=0; i<N; i++) { - String16 name(incl.getBasePackageName(i)); + const String16 name = incl.getBasePackageName(i); uint32_t id = incl.getBasePackageId(i); + // First time through: only add base packages (id // is not 0); second time through add the other // packages. @@ -1722,7 +1723,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets } } if (id != 0) { - NOISY(printf("Including package %s with ID=%d\n", + NOISY(fprintf(stderr, "Including package %s with ID=%d\n", String8(name).string(), id)); sp<Package> p = new Package(name, id); mPackages.add(name, p); @@ -2687,6 +2688,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) bool useUTF8 = !bundle->getUTF16StringsOption(); + // The libraries this table references. + Vector<sp<Package> > libraryPackages; + // Iterate through all data, collecting all values (strings, // references, etc). StringPool valueStrings(useUTF8); @@ -2694,8 +2698,22 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) for (pi=0; pi<N; pi++) { sp<Package> p = mOrderedPackages.itemAt(pi); if (p->getTypes().size() == 0) { - // Empty, skip! + // Empty, this is an imported package being used as + // a shared library. We do not flatten this package. + if (p->getAssignedId() != 0x01 && p->getName() != String16("android")) { + // This is not the base Android package, and it is a library + // so we must add a reference to the library when flattening. + libraryPackages.add(p); + } continue; + } else if (p->getAssignedId() == 0x00) { + if (!bundle->getBuildSharedLibrary()) { + fprintf(stderr, "ERROR: Package %s can not have ID=0x00 unless building a shared library.", + String8(p->getName()).string()); + return UNKNOWN_ERROR; + } + // If this is a shared library, we also include ourselves as an entry. + libraryPackages.add(p); } StringPool typeStrings(useUTF8); @@ -2778,7 +2796,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } ssize_t strAmt = 0; - + // Now build the array of package chunks. Vector<sp<AaptFile> > flatPackages; for (pi=0; pi<N; pi++) { @@ -2827,6 +2845,12 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return amt; } + err = flattenLibraryTable(data, libraryPackages); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: failed to write library table\n"); + return err; + } + // Build the type chunks inside of this package. for (size_t ti=0; ti<N; ti++) { // Retrieve them in the same order as the type string block. @@ -3053,7 +3077,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) fprintf(stderr, "**** value strings: %d\n", amt); fprintf(stderr, "**** total strings: %d\n", strAmt); #endif - + for (pi=0; pi<flatPackages.size(); pi++) { err = dest->writeData(flatPackages[pi]->getData(), flatPackages[pi]->getSize()); @@ -3078,6 +3102,38 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return NO_ERROR; } +status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) { + // Write out the library table if necessary + if (libs.size() > 0) { + NOISY(fprintf(stderr, "Writing library reference table\n")); + + const size_t libStart = dest->getSize(); + const size_t count = libs.size(); + ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(libStart, sizeof(ResTable_lib_header)); + + memset(libHeader, 0, sizeof(*libHeader)); + libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE); + libHeader->header.headerSize = htods(sizeof(*libHeader)); + libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count)); + libHeader->count = htodl(count); + + // Write the library entries + for (size_t i = 0; i < count; i++) { + const size_t entryStart = dest->getSize(); + sp<Package> libPackage = libs[i]; + NOISY(fprintf(stderr, " Entry %s -> 0x%02x\n", + String8(libPackage->getName()).string(), + (uint8_t)libPackage->getAssignedId())); + + ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(entryStart, sizeof(ResTable_lib_entry)); + memset(entry, 0, sizeof(*entry)); + entry->packageId = htodl(libPackage->getAssignedId()); + strcpy16_htod(entry->packageName, libPackage->getName().string()); + } + } + return NO_ERROR; +} + void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp) { fprintf(fp, @@ -3847,7 +3903,7 @@ sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package) return NULL; } mHaveAppPackage = true; - p = new Package(package, 127); + p = new Package(package, mIsSharedLibrary ? 0 : 127); } else { p = new Package(package, mNextPackageId); } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 75005cd..ec8fd17 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -224,6 +224,7 @@ public: status_t validateLocalizations(void); status_t flatten(Bundle*, const sp<AaptFile>& dest); + status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs); void writePublicDefinitions(const String16& package, FILE* fp); @@ -546,6 +547,7 @@ private: uint32_t mNextPackageId; bool mHaveAppPackage; bool mIsAppPackage; + bool mIsSharedLibrary; size_t mNumLocal; SourcePos mCurrentXmlPos; Bundle* mBundle; diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp index 4cf73d8..def6e2e 100644 --- a/tools/aapt/printapk.cpp +++ b/tools/aapt/printapk.cpp @@ -115,8 +115,8 @@ main(int argc, char** argv) size_t basePackageCount = res.getBasePackageCount(); printf("Base Packages: %d\n", (int)basePackageCount); for (size_t bpIndex=0; bpIndex<basePackageCount; bpIndex++) { - const char16_t* ch = res.getBasePackageName(bpIndex); - String8 s = String8(String16(ch)); + const String16 ch = res.getBasePackageName(bpIndex); + String8 s = String8(ch); printf(" [%3d] %s\n", (int)bpIndex, s.string()); } #endif |