Have R classes generate their own reference rewrite logic

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