Have R classes generate their own reference rewrite logic

Change-Id: I0e5b8311fc3479d966a49f9acf0d4c32a6a024d3
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;