summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/idmap/scan.cpp3
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/ContextImpl.java5
-rw-r--r--core/java/android/app/LoadedApk.java98
-rw-r--r--core/java/android/app/ResourcesManager.java19
-rw-r--r--core/java/android/content/res/AssetManager.java14
-rw-r--r--core/java/android/content/res/TypedArray.java2
-rw-r--r--core/java/android/content/res/XmlBlock.java10
-rw-r--r--core/jni/android_util_AssetManager.cpp50
-rw-r--r--core/jni/android_util_XmlBlock.cpp7
-rw-r--r--include/androidfw/AssetManager.h2
-rw-r--r--include/androidfw/ResourceTypes.h86
-rw-r--r--libs/androidfw/AssetManager.cpp45
-rw-r--r--libs/androidfw/ResourceTypes.cpp359
-rw-r--r--libs/androidfw/tests/ObbFile_test.cpp2
-rw-r--r--tests/SharedLibrary/client/AndroidManifest.xml2
-rw-r--r--tests/SharedLibrary/client/res/layout/main.xml25
-rw-r--r--tests/SharedLibrary/client/res/values/strings.xml1
-rw-r--r--tests/SharedLibrary/client/res/values/themes.xml6
-rw-r--r--tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java23
-rw-r--r--tests/SharedLibrary/lib/Android.mk7
-rw-r--r--tests/SharedLibrary/lib/proguard.proguard7
-rw-r--r--tests/SharedLibrary/lib/res/layout/address.xml16
-rw-r--r--tests/SharedLibrary/lib/res/values/attrs.xml13
-rw-r--r--tests/SharedLibrary/lib/res/values/public.xml17
-rw-r--r--tests/SharedLibrary/lib/res/values/strings.xml9
-rw-r--r--tests/SharedLibrary/lib/res/values/themes.xml18
-rw-r--r--tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java44
-rw-r--r--tools/aapt/AaptAssets.cpp5
-rw-r--r--tools/aapt/AaptAssets.h1
-rw-r--r--tools/aapt/Bundle.h4
-rw-r--r--tools/aapt/Main.cpp6
-rw-r--r--tools/aapt/ResourceIdCache.cpp8
-rw-r--r--tools/aapt/ResourceIdCache.h14
-rw-r--r--tools/aapt/ResourceTable.cpp70
-rw-r--r--tools/aapt/ResourceTable.h2
-rw-r--r--tools/aapt/printapk.cpp4
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