Have R classes generate their own reference rewrite logic
Change-Id: I0e5b8311fc3479d966a49f9acf0d4c32a6a024d3
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index e0c7816..d143b86 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -51,8 +51,8 @@
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.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
@@ -594,55 +594,6 @@
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) {
final Class<?> rClazz;
try {
@@ -650,35 +601,30 @@
} catch (ClassNotFoundException e) {
// This is not necessarily an error, as some packages do not ship with resources
// (or they do not need rewriting).
- Log.i(TAG, "Could not find R class for package '" + packageName + "'");
+ Log.i(TAG, "No resource references to update in package " + packageName);
return;
}
+ final Method callback;
try {
- 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);
+ callback = rClazz.getMethod("onResourcesLoaded", int.class);
+ } catch (NoSuchMethodException e) {
+ // No rewriting to be done.
+ return;
}
+
+ Throwable cause;
+ try {
+ callback.invoke(null, id);
+ return;
+ } catch (IllegalAccessException e) {
+ cause = e;
+ } catch (InvocationTargetException e) {
+ cause = e.getCause();
+ }
+
+ throw new RuntimeException("Failed to rewrite resource references for " + packageName,
+ cause);
}
public void removeContextRegistrations(Context context,
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index b394671..a0f0a08 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2488,10 +2488,12 @@
if (bundle->getCustomPackage() == NULL) {
// Write the R.java file into the appropriate class directory
// e.g. gen/com/foo/app/R.java
- err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
+ err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
+ bundle->getBuildSharedLibrary());
} else {
const String8 customPkg(bundle->getCustomPackage());
- err = writeResourceSymbols(bundle, assets, customPkg, true);
+ err = writeResourceSymbols(bundle, assets, customPkg, true,
+ bundle->getBuildSharedLibrary());
}
if (err < 0) {
goto bail;
@@ -2505,7 +2507,7 @@
char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
while (packageString != NULL) {
// Write the R.java file out with the correct package name
- err = writeResourceSymbols(bundle, assets, String8(packageString), true);
+ err = writeResourceSymbols(bundle, assets, String8(packageString), true, false);
if (err < 0) {
goto bail;
}
@@ -2514,11 +2516,11 @@
libs.unlockBuffer();
}
} else {
- err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
+ err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
if (err < 0) {
goto bail;
}
- err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
+ err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
if (err < 0) {
goto bail;
}
diff --git a/tools/aapt/IndentPrinter.h b/tools/aapt/IndentPrinter.h
new file mode 100644
index 0000000..6fc94bc
--- /dev/null
+++ b/tools/aapt/IndentPrinter.h
@@ -0,0 +1,63 @@
+#ifndef __INDENT_PRINTER_H
+#define __INDENT_PRINTER_H
+
+class IndentPrinter {
+public:
+ IndentPrinter(FILE* stream, int indentSize=2)
+ : mStream(stream)
+ , mIndentSize(indentSize)
+ , mIndent(0)
+ , mNeedsIndent(true) {
+ }
+
+ void indent(int amount = 1) {
+ mIndent += amount;
+ if (mIndent < 0) {
+ mIndent = 0;
+ }
+ }
+
+ void print(const char* fmt, ...) {
+ doIndent();
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(mStream, fmt, args);
+ va_end(args);
+ }
+
+ void println(const char* fmt, ...) {
+ doIndent();
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(mStream, fmt, args);
+ va_end(args);
+ fputs("\n", mStream);
+ mNeedsIndent = true;
+ }
+
+ void println() {
+ doIndent();
+ fputs("\n", mStream);
+ mNeedsIndent = true;
+ }
+
+private:
+ void doIndent() {
+ if (mNeedsIndent) {
+ int numSpaces = mIndent * mIndentSize;
+ while (numSpaces > 0) {
+ fputs(" ", mStream);
+ numSpaces--;
+ }
+ mNeedsIndent = false;
+ }
+ }
+
+ FILE* mStream;
+ const int mIndentSize;
+ int mIndent;
+ bool mNeedsIndent;
+};
+
+#endif // __INDENT_PRINTER_H
+
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index 34c4496..dd40b20 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -49,7 +49,8 @@
const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
extern android::status_t writeResourceSymbols(Bundle* bundle,
- const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
+ const sp<AaptAssets>& assets, const String8& pkgName,
+ bool includePrivate, bool emitCallback);
extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 1d93b89..7979a1d 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -3,18 +3,17 @@
//
// Build resource files from raw assets.
//
-#include "Main.h"
#include "AaptAssets.h"
-#include "StringPool.h"
-#include "XMLNode.h"
-#include "ResourceTable.h"
-#include "Images.h"
-
+#include "CacheUpdater.h"
#include "CrunchCache.h"
#include "FileFinder.h"
-#include "CacheUpdater.h"
-
+#include "Images.h"
+#include "IndentPrinter.h"
+#include "Main.h"
+#include "ResourceTable.h"
+#include "StringPool.h"
#include "WorkQueue.h"
+#include "XMLNode.h"
#if HAVE_PRINTF_ZD
# define ZD "%zd"
@@ -1801,6 +1800,112 @@
return String16();
}
+static void writeResourceLoadedCallback(FILE* fp, int indent) {
+ IndentPrinter p(fp, 4);
+ p.indent(indent);
+ p.println("private static void rewriteIntArrayField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {");
+ {
+ p.indent();
+ p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;");
+ p.println("if ((field.getModifiers() & requiredModifiers) != requiredModifiers) {");
+ {
+ p.indent();
+ p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println("if (field.getType() != int[].class) {");
+ {
+ p.indent();
+ p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int array\");");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println("int[] array = (int[]) field.get(null);");
+ p.println("for (int i = 0; i < array.length; i++) {");
+ {
+ p.indent();
+ p.println("array[i] = (array[i] & 0x00ffffff) | (packageId << 24);");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println();
+ p.println("private static void rewriteIntField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {");
+ {
+ p.indent();
+ p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;");
+ p.println("int bannedModifiers = java.lang.reflect.Modifier.FINAL;");
+ p.println("int mod = field.getModifiers();");
+ p.println("if ((mod & requiredModifiers) != requiredModifiers || (mod & bannedModifiers) != 0) {");
+ {
+ p.indent();
+ p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println("if (field.getType() != int.class && field.getType() != Integer.class) {");
+ {
+ p.indent();
+ p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int\");");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println("int resId = field.getInt(null);");
+ p.println("field.setInt(null, (resId & 0x00ffffff) | (packageId << 24));");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println();
+ p.println("public static void onResourcesLoaded(int assignedPackageId) throws Exception {");
+ {
+ p.indent();
+ p.println("Class<?>[] declaredClasses = R.class.getDeclaredClasses();");
+ p.println("for (Class<?> clazz : declaredClasses) {");
+ {
+ p.indent();
+ p.println("if (clazz.getSimpleName().equals(\"styleable\")) {");
+ {
+ p.indent();
+ p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {");
+ {
+ p.indent();
+ p.println("if (field.getType() == int[].class) {");
+ {
+ p.indent();
+ p.println("rewriteIntArrayField(field, assignedPackageId);");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("} else {");
+ {
+ p.indent();
+ p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {");
+ {
+ p.indent();
+ p.println("rewriteIntField(field, assignedPackageId);");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.indent(-1);
+ }
+ p.println("}");
+ p.println();
+}
+
static status_t writeLayoutClasses(
FILE* fp, const sp<AaptAssets>& assets,
const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId)
@@ -2138,7 +2243,7 @@
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
const sp<AaptSymbols>& symbols, const String8& className, int indent,
- bool nonConstantId)
+ bool nonConstantId, bool emitCallback)
{
fprintf(fp, "%spublic %sfinal class %s {\n",
getIndentSpace(indent),
@@ -2238,7 +2343,8 @@
if (nclassName == "styleable") {
styleableSymbols = nsymbols;
} else {
- err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
+ err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName,
+ indent, nonConstantId, false);
}
if (err != NO_ERROR) {
return err;
@@ -2252,6 +2358,10 @@
}
}
+ if (emitCallback) {
+ writeResourceLoadedCallback(fp, indent);
+ }
+
indent--;
fprintf(fp, "%s}\n", getIndentSpace(indent));
return NO_ERROR;
@@ -2299,7 +2409,7 @@
}
status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
- const String8& package, bool includePrivate)
+ const String8& package, bool includePrivate, bool emitCallback)
{
if (!bundle->getRClassDir()) {
return NO_ERROR;
@@ -2355,7 +2465,7 @@
"package %s;\n\n", package.string());
status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
- className, 0, bundle->getNonConstantId());
+ className, 0, bundle->getNonConstantId(), emitCallback);
fclose(fp);
if (err != NO_ERROR) {
return err;