auto import from //depot/cupcake/@135843
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
new file mode 100644
index 0000000..6bc1ee6
--- /dev/null
+++ b/tools/aapt/AaptAssets.cpp
@@ -0,0 +1,1717 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+
+#include "AaptAssets.h"
+#include "Main.h"
+
+#include <utils/misc.h>
+#include <utils/SortedVector.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+
+static const char* kDefaultLocale = "default";
+static const char* kWildcardName = "any";
+static const char* kAssetDir = "assets";
+static const char* kResourceDir = "res";
+static const char* kInvalidChars = "/\\:";
+static const size_t kMaxAssetFileName = 100;
+
+static const String8 kResString(kResourceDir);
+
+/*
+ * Names of asset files must meet the following criteria:
+ *
+ *  - the filename length must be less than kMaxAssetFileName bytes long
+ *    (and can't be empty)
+ *  - all characters must be 7-bit printable ASCII
+ *  - none of { '/' '\\' ':' }
+ *
+ * Pass in just the filename, not the full path.
+ */
+static bool validateFileName(const char* fileName)
+{
+    const char* cp = fileName;
+    size_t len = 0;
+
+    while (*cp != '\0') {
+        if ((*cp & 0x80) != 0)
+            return false;           // reject high ASCII
+        if (*cp < 0x20 || *cp >= 0x7f)
+            return false;           // reject control chars and 0x7f
+        if (strchr(kInvalidChars, *cp) != NULL)
+            return false;           // reject path sep chars
+        cp++;
+        len++;
+    }
+
+    if (len < 1 || len > kMaxAssetFileName)
+        return false;               // reject empty or too long
+
+    return true;
+}
+
+static bool isHidden(const char *root, const char *path)
+{
+    const char *type = NULL;
+
+    // Skip all hidden files.
+    if (path[0] == '.') {
+        // Skip ., .. and  .svn but don't chatter about it.
+        if (strcmp(path, ".") == 0
+            || strcmp(path, "..") == 0
+            || strcmp(path, ".svn") == 0) {
+            return true;
+        }
+        type = "hidden";
+    } else if (path[0] == '_') {
+        // skip directories starting with _ (don't chatter about it)
+        String8 subdirName(root);
+        subdirName.appendPath(path);
+        if (getFileType(subdirName.string()) == kFileTypeDirectory) {
+            return true;
+        }
+    } else if (strcmp(path, "CVS") == 0) {
+        // Skip CVS but don't chatter about it.
+        return true;
+    } else if (strcasecmp(path, "thumbs.db") == 0
+               || strcasecmp(path, "picasa.ini") == 0) {
+        // Skip suspected image indexes files.
+        type = "index";
+    } else if (path[strlen(path)-1] == '~') {
+        // Skip suspected emacs backup files.
+        type = "backup";
+    } else {
+        // Let everything else through.
+        return false;
+    }
+
+    /* If we get this far, "type" should be set and the file
+     * should be skipped.
+     */
+    String8 subdirName(root);
+    subdirName.appendPath(path);
+    fprintf(stderr, "    (skipping %s %s '%s')\n", type,
+            getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
+            subdirName.string());
+
+    return true;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t
+AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
+{
+    ResTable_config config;
+
+    // IMSI - MCC
+    if (getMccName(part.string(), &config)) {
+        *axis = AXIS_MCC;
+        *value = config.mcc;
+        return 0;
+    }
+
+    // IMSI - MNC
+    if (getMncName(part.string(), &config)) {
+        *axis = AXIS_MNC;
+        *value = config.mnc;
+        return 0;
+    }
+
+    // locale - language
+    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
+        *axis = AXIS_LANGUAGE;
+        *value = part[1] << 8 | part[0];
+        return 0;
+    }
+
+    // locale - language_REGION
+    if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
+            && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
+        *axis = AXIS_LANGUAGE;
+        *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
+        return 0;
+    }
+
+    // orientation
+    if (getOrientationName(part.string(), &config)) {
+        *axis = AXIS_ORIENTATION;
+        *value = config.orientation;
+        return 0;
+    }
+
+    // density
+    if (getDensityName(part.string(), &config)) {
+        *axis = AXIS_DENSITY;
+        *value = config.density;
+        return 0;
+    }
+
+    // touchscreen
+    if (getTouchscreenName(part.string(), &config)) {
+        *axis = AXIS_TOUCHSCREEN;
+        *value = config.touchscreen;
+        return 0;
+    }
+
+    // keyboard hidden
+    if (getKeysHiddenName(part.string(), &config)) {
+        *axis = AXIS_KEYSHIDDEN;
+        *value = config.inputFlags;
+        return 0;
+    }
+
+    // keyboard
+    if (getKeyboardName(part.string(), &config)) {
+        *axis = AXIS_KEYBOARD;
+        *value = config.keyboard;
+        return 0;
+    }
+
+    // navigation
+    if (getNavigationName(part.string(), &config)) {
+        *axis = AXIS_NAVIGATION;
+        *value = config.navigation;
+        return 0;
+    }
+
+    // screen size
+    if (getScreenSizeName(part.string(), &config)) {
+        *axis = AXIS_SCREENSIZE;
+        *value = config.screenSize;
+        return 0;
+    }
+
+    // version
+    if (getVersionName(part.string(), &config)) {
+        *axis = AXIS_VERSION;
+        *value = config.version;
+        return 0;
+    }
+
+    return 1;
+}
+
+bool
+AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
+{
+    Vector<String8> parts;
+
+    String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers;
+
+    const char *p = dir;
+    const char *q;
+    while (NULL != (q = strchr(p, '-'))) {
+        String8 val(p, q-p);
+        val.toLower();
+        parts.add(val);
+        //printf("part: %s\n", parts[parts.size()-1].string());
+        p = q+1;
+    }
+    String8 val(p);
+    val.toLower();
+    parts.add(val);
+    //printf("part: %s\n", parts[parts.size()-1].string());
+
+    const int N = parts.size();
+    int index = 0;
+    String8 part = parts[index];
+
+    // resource type
+    if (!isValidResourceType(part)) {
+        return false;
+    }
+    *resType = part;
+
+    index++;
+    if (index == N) {
+        goto success;
+    }
+    part = parts[index];
+
+    // imsi - mcc
+    if (getMccName(part.string())) {
+        mcc = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not mcc: %s\n", part.string());
+    }
+
+    // imsi - mnc
+    if (getMncName(part.string())) {
+        mnc = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not mcc: %s\n", part.string());
+    }
+
+    // locale - language
+    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
+        loc = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not language: %s\n", part.string());
+    }
+
+    // locale - region
+    if (loc.length() > 0
+            && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
+        loc += "-";
+        part.toUpper();
+        loc += part.string() + 1;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not region: %s\n", part.string());
+    }
+
+    // orientation
+    if (getOrientationName(part.string())) {
+        orient = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not orientation: %s\n", part.string());
+    }
+
+    // density
+    if (getDensityName(part.string())) {
+        den = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not density: %s\n", part.string());
+    }
+
+    // touchscreen
+    if (getTouchscreenName(part.string())) {
+        touch = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not touchscreen: %s\n", part.string());
+    }
+
+    // keyboard hidden
+    if (getKeysHiddenName(part.string())) {
+        keysHidden = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not keysHidden: %s\n", part.string());
+    }
+
+    // keyboard
+    if (getKeyboardName(part.string())) {
+        key = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not keyboard: %s\n", part.string());
+    }
+
+    if (getNavigationName(part.string())) {
+        nav = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not navigation: %s\n", part.string());
+    }
+
+    if (getScreenSizeName(part.string())) {
+        size = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not screen size: %s\n", part.string());
+    }
+
+    if (getVersionName(part.string())) {
+        vers = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not version: %s\n", part.string());
+    }
+
+    // if there are extra parts, it doesn't match
+    return false;
+
+success:
+    this->mcc = mcc;
+    this->mnc = mnc;
+    this->locale = loc;
+    this->orientation = orient;
+    this->density = den;
+    this->touchscreen = touch;
+    this->keysHidden = keysHidden;
+    this->keyboard = key;
+    this->navigation = nav;
+    this->screenSize = size;
+    this->version = vers;
+
+    // what is this anyway?
+    this->vendor = "";
+
+    return true;
+}
+
+String8
+AaptGroupEntry::toString() const
+{
+    String8 s = this->mcc;
+    s += ",";
+    s += this->mnc;
+    s += ",";
+    s += this->locale;
+    s += ",";
+    s += this->orientation;
+    s += ",";
+    s += density;
+    s += ",";
+    s += touchscreen;
+    s += ",";
+    s += keysHidden;
+    s += ",";
+    s += keyboard;
+    s += ",";
+    s += navigation;
+    s += ",";
+    s += screenSize;
+    s += ",";
+    s += version;
+    return s;
+}
+
+String8
+AaptGroupEntry::toDirName(const String8& resType) const
+{
+    String8 s = resType;
+    if (this->mcc != "") {
+        s += "-";
+        s += mcc;
+    }
+    if (this->mnc != "") {
+        s += "-";
+        s += mnc;
+    }
+    if (this->locale != "") {
+        s += "-";
+        s += locale;
+    }
+    if (this->orientation != "") {
+        s += "-";
+        s += orientation;
+    }
+    if (this->density != "") {
+        s += "-";
+        s += density;
+    }
+    if (this->touchscreen != "") {
+        s += "-";
+        s += touchscreen;
+    }
+    if (this->keysHidden != "") {
+        s += "-";
+        s += keysHidden;
+    }
+    if (this->keyboard != "") {
+        s += "-";
+        s += keyboard;
+    }
+    if (this->navigation != "") {
+        s += "-";
+        s += navigation;
+    }
+    if (this->screenSize != "") {
+        s += "-";
+        s += screenSize;
+    }
+    if (this->version != "") {
+        s += "-";
+        s += version;
+    }
+
+    return s;
+}
+
+bool AaptGroupEntry::getMccName(const char* name,
+                                    ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val != 3) return false;
+
+    int d = atoi(val);
+    if (d != 0) {
+        if (out) out->mcc = d;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getMncName(const char* name,
+                                    ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'n') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val == 0 || c-val > 3) return false;
+
+    int d = atoi(val);
+    if (d != 0) {
+        if (out) out->mnc = d;
+        return true;
+    }
+
+    return false;
+}
+
+/*
+ * Does this directory name fit the pattern of a locale dir ("en-rUS" or
+ * "default")?
+ *
+ * TODO: Should insist that the first two letters are lower case, and the
+ * second two are upper.
+ */
+bool AaptGroupEntry::getLocaleName(const char* fileName,
+                                   ResTable_config* out)
+{
+    if (strcmp(fileName, kWildcardName) == 0
+            || strcmp(fileName, kDefaultLocale) == 0) {
+        if (out) {
+            out->language[0] = 0;
+            out->language[1] = 0;
+            out->country[0] = 0;
+            out->country[1] = 0;
+        }
+        return true;
+    }
+
+    if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
+        if (out) {
+            out->language[0] = fileName[0];
+            out->language[1] = fileName[1];
+            out->country[0] = 0;
+            out->country[1] = 0;
+        }
+        return true;
+    }
+
+    if (strlen(fileName) == 5 &&
+        isalpha(fileName[0]) &&
+        isalpha(fileName[1]) &&
+        fileName[2] == '-' &&
+        isalpha(fileName[3]) &&
+        isalpha(fileName[4])) {
+        if (out) {
+            out->language[0] = fileName[0];
+            out->language[1] = fileName[1];
+            out->country[0] = fileName[3];
+            out->country[1] = fileName[4];
+        }
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getOrientationName(const char* name,
+                                        ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->orientation = out->ORIENTATION_ANY;
+        return true;
+    } else if (strcmp(name, "port") == 0) {
+        if (out) out->orientation = out->ORIENTATION_PORT;
+        return true;
+    } else if (strcmp(name, "land") == 0) {
+        if (out) out->orientation = out->ORIENTATION_LAND;
+        return true;
+    } else if (strcmp(name, "square") == 0) {
+        if (out) out->orientation = out->ORIENTATION_SQUARE;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getDensityName(const char* name,
+                                    ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->density = 0;
+        return true;
+    }
+    char* c = (char*)name;
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+
+    // check that we have 'dpi' after the last digit.
+    if (toupper(c[0]) != 'D' ||
+            toupper(c[1]) != 'P' ||
+            toupper(c[2]) != 'I' ||
+            c[3] != 0) {
+        return false;
+    }
+
+    // temporarily replace the first letter with \0 to
+    // use atoi.
+    char tmp = c[0];
+    c[0] = '\0';
+
+    int d = atoi(name);
+    c[0] = tmp;
+
+    if (d != 0) {
+        if (out) out->density = d;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getTouchscreenName(const char* name,
+                                        ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+        return true;
+    } else if (strcmp(name, "notouch") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+        return true;
+    } else if (strcmp(name, "stylus") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+        return true;
+    } else if (strcmp(name, "finger") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getKeysHiddenName(const char* name,
+                                       ResTable_config* out)
+{
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = out->MASK_KEYSHIDDEN;
+        value = out->KEYSHIDDEN_ANY;
+    } else if (strcmp(name, "keysexposed") == 0) {
+        mask = out->MASK_KEYSHIDDEN;
+        value = out->KEYSHIDDEN_NO;
+    } else if (strcmp(name, "keyshidden") == 0) {
+        mask = out->MASK_KEYSHIDDEN;
+        value = out->KEYSHIDDEN_YES;
+    } else if (strcmp(name, "keyssoft") == 0) {
+        mask = out->MASK_KEYSHIDDEN;
+        value = out->KEYSHIDDEN_SOFT;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getKeyboardName(const char* name,
+                                        ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->keyboard = out->KEYBOARD_ANY;
+        return true;
+    } else if (strcmp(name, "nokeys") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+        return true;
+    } else if (strcmp(name, "qwerty") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_QWERTY;
+        return true;
+    } else if (strcmp(name, "12key") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_12KEY;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getNavigationName(const char* name,
+                                     ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->navigation = out->NAVIGATION_ANY;
+        return true;
+    } else if (strcmp(name, "nonav") == 0) {
+        if (out) out->navigation = out->NAVIGATION_NONAV;
+        return true;
+    } else if (strcmp(name, "dpad") == 0) {
+        if (out) out->navigation = out->NAVIGATION_DPAD;
+        return true;
+    } else if (strcmp(name, "trackball") == 0) {
+        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+        return true;
+    } else if (strcmp(name, "wheel") == 0) {
+        if (out) out->navigation = out->NAVIGATION_WHEEL;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getScreenSizeName(const char* name,
+                                       ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidth = out->SCREENWIDTH_ANY;
+            out->screenHeight = out->SCREENHEIGHT_ANY;
+        }
+        return true;
+    }
+
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || *x != 'x') return false;
+    String8 xName(name, x-name);
+    x++;
+
+    const char* y = x;
+    while (*y >= '0' && *y <= '9') y++;
+    if (y == name || *y != 0) return false;
+    String8 yName(x, y-x);
+
+    uint16_t w = (uint16_t)atoi(xName.string());
+    uint16_t h = (uint16_t)atoi(yName.string());
+    if (w < h) {
+        return false;
+    }
+
+    if (out) {
+        out->screenWidth = w;
+        out->screenHeight = h;
+    }
+
+    return true;
+}
+
+bool AaptGroupEntry::getVersionName(const char* name,
+                                    ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->sdkVersion = out->SDKVERSION_ANY;
+            out->minorVersion = out->MINORVERSION_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'v') {
+        return false;
+    }
+
+    name++;
+    const char* s = name;
+    while (*s >= '0' && *s <= '9') s++;
+    if (s == name || *s != 0) return false;
+    String8 sdkName(name, s-name);
+
+    if (out) {
+        out->sdkVersion = (uint16_t)atoi(sdkName.string());
+        out->minorVersion = 0;
+    }
+
+    return true;
+}
+
+int AaptGroupEntry::compare(const AaptGroupEntry& o) const
+{
+    int v = mcc.compare(o.mcc);
+    if (v == 0) v = mnc.compare(o.mnc);
+    if (v == 0) v = locale.compare(o.locale);
+    if (v == 0) v = vendor.compare(o.vendor);
+    if (v == 0) v = orientation.compare(o.orientation);
+    if (v == 0) v = density.compare(o.density);
+    if (v == 0) v = touchscreen.compare(o.touchscreen);
+    if (v == 0) v = keysHidden.compare(o.keysHidden);
+    if (v == 0) v = keyboard.compare(o.keyboard);
+    if (v == 0) v = navigation.compare(o.navigation);
+    if (v == 0) v = screenSize.compare(o.screenSize);
+    if (v == 0) v = version.compare(o.version);
+    return v;
+}
+
+ResTable_config AaptGroupEntry::toParams() const
+{
+    ResTable_config params;
+    memset(&params, 0, sizeof(params));
+    getMccName(mcc.string(), &params);
+    getMncName(mnc.string(), &params);
+    getLocaleName(locale.string(), &params);
+    getOrientationName(orientation.string(), &params);
+    getDensityName(density.string(), &params);
+    getTouchscreenName(touchscreen.string(), &params);
+    getKeysHiddenName(keysHidden.string(), &params);
+    getKeyboardName(keyboard.string(), &params);
+    getNavigationName(navigation.string(), &params);
+    getScreenSizeName(screenSize.string(), &params);
+    getVersionName(version.string(), &params);
+    return params;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+void* AaptFile::editData(size_t size)
+{
+    if (size <= mBufferSize) {
+        mDataSize = size;
+        return mData;
+    }
+    size_t allocSize = (size*3)/2;
+    void* buf = realloc(mData, allocSize);
+    if (buf == NULL) {
+        return NULL;
+    }
+    mData = buf;
+    mDataSize = size;
+    mBufferSize = allocSize;
+    return buf;
+}
+
+void* AaptFile::editData(size_t* outSize)
+{
+    if (outSize) {
+        *outSize = mDataSize;
+    }
+    return mData;
+}
+
+void* AaptFile::padData(size_t wordSize)
+{
+    const size_t extra = mDataSize%wordSize;
+    if (extra == 0) {
+        return mData;
+    }
+
+    size_t initial = mDataSize;
+    void* data = editData(initial+(wordSize-extra));
+    if (data != NULL) {
+        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
+    }
+    return data;
+}
+
+status_t AaptFile::writeData(const void* data, size_t size)
+{
+    size_t end = mDataSize;
+    size_t total = size + end;
+    void* buf = editData(total);
+    if (buf == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    memcpy(((char*)buf)+end, data, size);
+    return NO_ERROR;
+}
+
+void AaptFile::clearData()
+{
+    if (mData != NULL) free(mData);
+    mData = NULL;
+    mDataSize = 0;
+    mBufferSize = 0;
+}
+
+String8 AaptFile::getPrintableSource() const
+{
+    if (hasData()) {
+        String8 name(mGroupEntry.locale.string());
+        name.appendPath(mGroupEntry.vendor.string());
+        name.appendPath(mPath);
+        name.append(" #generated");
+        return name;
+    }
+    return mSourceFile;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t AaptGroup::addFile(const sp<AaptFile>& file)
+{
+    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
+        file->mPath = mPath;
+        mFiles.add(file->getGroupEntry(), file);
+        return NO_ERROR;
+    }
+
+    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
+                                               getPrintableSource().string());
+    return UNKNOWN_ERROR;
+}
+
+void AaptGroup::removeFile(size_t index)
+{
+	mFiles.removeItemsAt(index);
+}
+
+void AaptGroup::print() const
+{
+    printf("  %s\n", getPath().string());
+    const size_t N=mFiles.size();
+    size_t i;
+    for (i=0; i<N; i++) {
+        sp<AaptFile> file = mFiles.valueAt(i);
+        const AaptGroupEntry& e = file->getGroupEntry();
+        if (file->hasData()) {
+            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
+                    (int)file->getSize());
+        } else {
+            printf("      Src: %s\n", file->getPrintableSource().string());
+        }
+    }
+}
+
+String8 AaptGroup::getPrintableSource() const
+{
+    if (mFiles.size() > 0) {
+        // Arbitrarily pull the first source file out of the list.
+        return mFiles.valueAt(0)->getPrintableSource();
+    }
+
+    // Should never hit this case, but to be safe...
+    return getPath();
+
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
+{
+    if (mFiles.indexOfKey(name) >= 0) {
+        return ALREADY_EXISTS;
+    }
+    mFiles.add(name, file);
+    return NO_ERROR;
+}
+
+status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
+{
+    if (mDirs.indexOfKey(name) >= 0) {
+        return ALREADY_EXISTS;
+    }
+    mDirs.add(name, dir);
+    return NO_ERROR;
+}
+
+sp<AaptDir> AaptDir::makeDir(const String8& path)
+{
+    String8 name;
+    String8 remain = path;
+
+    sp<AaptDir> subdir = this;
+    while (name = remain.walkPath(&remain), remain != "") {
+        subdir = subdir->makeDir(name);
+    }
+
+    ssize_t i = subdir->mDirs.indexOfKey(name);
+    if (i >= 0) {
+        return subdir->mDirs.valueAt(i);
+    }
+    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
+    subdir->mDirs.add(name, dir);
+    return dir;
+}
+
+void AaptDir::removeFile(const String8& name)
+{
+    mFiles.removeItem(name);
+}
+
+void AaptDir::removeDir(const String8& name)
+{
+    mDirs.removeItem(name);
+}
+
+status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
+{
+	sp<AaptGroup> origGroup;
+
+	// Find and remove the given file with shear, brute force!
+	const size_t NG = mFiles.size();
+	size_t i;
+	for (i=0; origGroup == NULL && i<NG; i++) {
+		sp<AaptGroup> g = mFiles.valueAt(i);
+		const size_t NF = g->getFiles().size();
+		for (size_t j=0; j<NF; j++) {
+			if (g->getFiles().valueAt(j) == file) {
+				origGroup = g;
+				g->removeFile(j);
+				if (NF == 1) {
+					mFiles.removeItemsAt(i);
+				}
+				break;
+			}
+		}
+	}
+
+	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
+
+	// Place the file under its new name.
+	if (origGroup != NULL) {
+		return addLeafFile(newName, file);
+	}
+
+	return NO_ERROR;
+}
+
+status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
+{
+    sp<AaptGroup> group;
+    if (mFiles.indexOfKey(leafName) >= 0) {
+        group = mFiles.valueFor(leafName);
+    } else {
+        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
+        mFiles.add(leafName, group);
+    }
+
+    return group->addFile(file);
+}
+
+ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
+                            const AaptGroupEntry& kind, const String8& resType)
+{
+    Vector<String8> fileNames;
+
+    {
+        DIR* dir = NULL;
+
+        dir = opendir(srcDir.string());
+        if (dir == NULL) {
+            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+            return UNKNOWN_ERROR;
+        }
+
+        /*
+         * Slurp the filenames out of the directory.
+         */
+        while (1) {
+            struct dirent* entry;
+
+            entry = readdir(dir);
+            if (entry == NULL)
+                break;
+
+            if (isHidden(srcDir.string(), entry->d_name))
+                continue;
+
+            fileNames.add(String8(entry->d_name));
+        }
+
+        closedir(dir);
+    }
+
+    ssize_t count = 0;
+
+    /*
+     * Stash away the files and recursively descend into subdirectories.
+     */
+    const size_t N = fileNames.size();
+    size_t i;
+    for (i = 0; i < N; i++) {
+        String8 pathName(srcDir);
+        FileType type;
+
+        pathName.appendPath(fileNames[i].string());
+        type = getFileType(pathName.string());
+        if (type == kFileTypeDirectory) {
+            sp<AaptDir> subdir;
+            bool notAdded = false;
+            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
+                subdir = mDirs.valueFor(fileNames[i]);
+            } else {
+                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
+                notAdded = true;
+            }
+            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
+                                                resType);
+            if (res < NO_ERROR) {
+                return res;
+            }
+            if (res > 0 && notAdded) {
+                mDirs.add(fileNames[i], subdir);
+            }
+            count += res;
+        } else if (type == kFileTypeRegular) {
+            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
+            status_t err = addLeafFile(fileNames[i], file);
+            if (err != NO_ERROR) {
+                return err;
+            }
+
+            count++;
+
+        } else {
+            if (bundle->getVerbose())
+                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
+        }
+    }
+
+    return count;
+}
+
+status_t AaptDir::validate() const
+{
+    const size_t NF = mFiles.size();
+    const size_t ND = mDirs.size();
+    size_t i;
+    for (i = 0; i < NF; i++) {
+        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
+            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+                    "Invalid filename.  Unable to add.");
+            return UNKNOWN_ERROR;
+        }
+
+        size_t j;
+        for (j = i+1; j < NF; j++) {
+            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
+                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
+                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+                        "File is case-insensitive equivalent to: %s",
+                        mFiles.valueAt(j)->getPrintableSource().string());
+                return UNKNOWN_ERROR;
+            }
+
+            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
+            // (this is mostly caught by the "marked" stuff, below)
+        }
+
+        for (j = 0; j < ND; j++) {
+            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
+                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
+                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+                        "File conflicts with dir from: %s",
+                        mDirs.valueAt(j)->getPrintableSource().string());
+                return UNKNOWN_ERROR;
+            }
+        }
+    }
+
+    for (i = 0; i < ND; i++) {
+        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
+            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
+                    "Invalid directory name, unable to add.");
+            return UNKNOWN_ERROR;
+        }
+
+        size_t j;
+        for (j = i+1; j < ND; j++) {
+            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
+                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
+                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
+                        "Directory is case-insensitive equivalent to: %s",
+                        mDirs.valueAt(j)->getPrintableSource().string());
+                return UNKNOWN_ERROR;
+            }
+        }
+
+        status_t err = mDirs.valueAt(i)->validate();
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void AaptDir::print() const
+{
+    const size_t ND=getDirs().size();
+    size_t i;
+    for (i=0; i<ND; i++) {
+        getDirs().valueAt(i)->print();
+    }
+
+    const size_t NF=getFiles().size();
+    for (i=0; i<NF; i++) {
+        getFiles().valueAt(i)->print();
+    }
+}
+
+String8 AaptDir::getPrintableSource() const
+{
+    if (mFiles.size() > 0) {
+        // Arbitrarily pull the first file out of the list as the source dir.
+        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
+    }
+    if (mDirs.size() > 0) {
+        // Or arbitrarily pull the first dir out of the list as the source dir.
+        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
+    }
+
+    // Should never hit this case, but to be safe...
+    return mPath;
+
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+sp<AaptFile> AaptAssets::addFile(
+        const String8& filePath, const AaptGroupEntry& entry,
+        const String8& srcDir, sp<AaptGroup>* outGroup,
+        const String8& resType)
+{
+    sp<AaptDir> dir = this;
+    sp<AaptGroup> group;
+    sp<AaptFile> file;
+    String8 root, remain(filePath), partialPath;
+    while (remain.length() > 0) {
+        root = remain.walkPath(&remain);
+        partialPath.appendPath(root);
+
+        const String8 rootStr(root);
+
+        if (remain.length() == 0) {
+            ssize_t i = dir->getFiles().indexOfKey(rootStr);
+            if (i >= 0) {
+                group = dir->getFiles().valueAt(i);
+            } else {
+                group = new AaptGroup(rootStr, filePath);
+                status_t res = dir->addFile(rootStr, group);
+                if (res != NO_ERROR) {
+                    return NULL;
+                }
+            }
+            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
+            status_t res = group->addFile(file);
+            if (res != NO_ERROR) {
+                return NULL;
+            }
+            break;
+
+        } else {
+            ssize_t i = dir->getDirs().indexOfKey(rootStr);
+            if (i >= 0) {
+                dir = dir->getDirs().valueAt(i);
+            } else {
+                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
+                status_t res = dir->addDir(rootStr, subdir);
+                if (res != NO_ERROR) {
+                    return NULL;
+                }
+                dir = subdir;
+            }
+        }
+    }
+
+    mGroupEntries.add(entry);
+    if (outGroup) *outGroup = group;
+    return file;
+}
+
+void AaptAssets::addResource(const String8& leafName, const String8& path,
+                const sp<AaptFile>& file, const String8& resType)
+{
+    sp<AaptDir> res = AaptDir::makeDir(kResString);
+    String8 dirname = file->getGroupEntry().toDirName(resType);
+    sp<AaptDir> subdir = res->makeDir(dirname);
+    sp<AaptGroup> grr = new AaptGroup(leafName, path);
+    grr->addFile(file);
+
+    subdir->addFile(leafName, grr);
+}
+
+
+ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
+{
+    int count;
+    int totalCount = 0;
+    FileType type;
+    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
+    const size_t dirCount =resDirs.size();
+    sp<AaptAssets> current = this;
+
+    const int N = bundle->getFileSpecCount();
+
+    /*
+     * If a package manifest was specified, include that first.
+     */
+    if (bundle->getAndroidManifestFile() != NULL) {
+        // place at root of zip.
+        String8 srcFile(bundle->getAndroidManifestFile());
+        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
+                NULL, String8());
+        totalCount++;
+    }
+
+    /*
+     * If a directory of custom assets was supplied, slurp 'em up.
+     */
+    if (bundle->getAssetSourceDir()) {
+        const char* assetDir = bundle->getAssetSourceDir();
+
+        FileType type = getFileType(assetDir);
+        if (type == kFileTypeNonexistent) {
+            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
+            return UNKNOWN_ERROR;
+        }
+        if (type != kFileTypeDirectory) {
+            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
+            return UNKNOWN_ERROR;
+        }
+
+        String8 assetRoot(assetDir);
+        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
+        AaptGroupEntry group;
+        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
+                                            String8());
+        if (count < 0) {
+            totalCount = count;
+            goto bail;
+        }
+        if (count > 0) {
+            mGroupEntries.add(group);
+        }
+        totalCount += count;
+
+        if (bundle->getVerbose())
+            printf("Found %d custom asset file%s in %s\n",
+                   count, (count==1) ? "" : "s", assetDir);
+    }
+
+    /*
+     * If a directory of resource-specific assets was supplied, slurp 'em up.
+     */
+    for (size_t i=0; i<dirCount; i++) {
+        const char *res = resDirs[i];
+        if (res) {
+            type = getFileType(res);
+            if (type == kFileTypeNonexistent) {
+                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
+                return UNKNOWN_ERROR;
+            }
+            if (type == kFileTypeDirectory) {
+                if (i>0) {
+                    sp<AaptAssets> nextOverlay = new AaptAssets();
+                    current->setOverlay(nextOverlay);
+                    current = nextOverlay;
+                }
+                count = current->slurpResourceTree(bundle, String8(res));
+
+                if (count < 0) {
+                    totalCount = count;
+                    goto bail;
+                }
+                totalCount += count;
+            }
+            else {
+                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
+                return UNKNOWN_ERROR;
+            }
+        }
+        
+    }
+    /*
+     * Now do any additional raw files.
+     */
+    for (int arg=0; arg<N; arg++) {
+        const char* assetDir = bundle->getFileSpecEntry(arg);
+
+        FileType type = getFileType(assetDir);
+        if (type == kFileTypeNonexistent) {
+            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
+            return UNKNOWN_ERROR;
+        }
+        if (type != kFileTypeDirectory) {
+            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
+            return UNKNOWN_ERROR;
+        }
+
+        String8 assetRoot(assetDir);
+
+        if (bundle->getVerbose())
+            printf("Processing raw dir '%s'\n", (const char*) assetDir);
+
+        /*
+         * Do a recursive traversal of subdir tree.  We don't make any
+         * guarantees about ordering, so we're okay with an inorder search
+         * using whatever order the OS happens to hand back to us.
+         */
+        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
+        if (count < 0) {
+            /* failure; report error and remove archive */
+            totalCount = count;
+            goto bail;
+        }
+        totalCount += count;
+
+        if (bundle->getVerbose())
+            printf("Found %d asset file%s in %s\n",
+                   count, (count==1) ? "" : "s", assetDir);
+    }
+
+    count = validate();
+    if (count != NO_ERROR) {
+        totalCount = count;
+        goto bail;
+    }
+
+
+bail:
+    return totalCount;
+}
+
+ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
+                                    const AaptGroupEntry& kind,
+                                    const String8& resType)
+{
+    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
+    if (res > 0) {
+        mGroupEntries.add(kind);
+    }
+
+    return res;
+}
+
+ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
+{
+    ssize_t err = 0;
+
+    DIR* dir = opendir(srcDir.string());
+    if (dir == NULL) {
+        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+        return UNKNOWN_ERROR;
+    }
+
+    status_t count = 0;
+
+    /*
+     * Run through the directory, looking for dirs that match the
+     * expected pattern.
+     */
+    while (1) {
+        struct dirent* entry = readdir(dir);
+        if (entry == NULL) {
+            break;
+        }
+
+        if (isHidden(srcDir.string(), entry->d_name)) {
+            continue;
+        }
+
+        String8 subdirName(srcDir);
+        subdirName.appendPath(entry->d_name);
+
+        AaptGroupEntry group;
+        String8 resType;
+        bool b = group.initFromDirName(entry->d_name, &resType);
+        if (!b) {
+            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+                    entry->d_name);
+            err = -1;
+            continue;
+        }
+
+        FileType type = getFileType(subdirName.string());
+
+        if (type == kFileTypeDirectory) {
+            sp<AaptDir> dir = makeDir(String8(entry->d_name));
+            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
+                                                resType);
+            if (res < 0) {
+                count = res;
+                goto bail;
+            }
+            if (res > 0) {
+                mGroupEntries.add(group);
+                count += res;
+            }
+
+            mDirs.add(dir);
+        } else {
+            if (bundle->getVerbose()) {
+                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
+            }
+        }
+    }
+
+bail:
+    closedir(dir);
+    dir = NULL;
+
+    if (err != 0) {
+        return err;
+    }
+    return count;
+}
+
+ssize_t
+AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
+{
+    int count = 0;
+    SortedVector<AaptGroupEntry> entries;
+
+    ZipFile* zip = new ZipFile;
+    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "error opening zip file %s\n", filename);
+        count = err;
+        delete zip;
+        return -1;
+    }
+
+    const int N = zip->getNumEntries();
+    for (int i=0; i<N; i++) {
+        ZipEntry* entry = zip->getEntryByIndex(i);
+        if (entry->getDeleted()) {
+            continue;
+        }
+
+        String8 entryName(entry->getFileName());
+
+        String8 dirName = entryName.getPathDir();
+        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
+
+        String8 resType;
+        AaptGroupEntry kind;
+
+        String8 remain;
+        if (entryName.walkPath(&remain) == kResourceDir) {
+            // these are the resources, pull their type out of the directory name
+            kind.initFromDirName(remain.walkPath().string(), &resType);
+        } else {
+            // these are untyped and don't have an AaptGroupEntry
+        }
+        if (entries.indexOf(kind) < 0) {
+            entries.add(kind);
+            mGroupEntries.add(kind);
+        }
+
+        // use the one from the zip file if they both exist.
+        dir->removeFile(entryName.getPathLeaf());
+
+        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
+        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
+        if (err != NO_ERROR) {
+            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
+            count = err;
+            goto bail;
+        }
+        file->setCompressionMethod(entry->getCompressionMethod());
+
+#if 0
+        if (entryName == "AndroidManifest.xml") {
+            printf("AndroidManifest.xml\n");
+        }
+        printf("\n\nfile: %s\n", entryName.string());
+#endif
+
+        size_t len = entry->getUncompressedLen();
+        void* data = zip->uncompress(entry);
+        void* buf = file->editData(len);
+        memcpy(buf, data, len);
+
+#if 0
+        const int OFF = 0;
+        const unsigned char* p = (unsigned char*)data;
+        const unsigned char* end = p+len;
+        p += OFF;
+        for (int i=0; i<32 && p < end; i++) {
+            printf("0x%03x ", i*0x10 + OFF);
+            for (int j=0; j<0x10 && p < end; j++) {
+                printf(" %02x", *p);
+                p++;
+            }
+            printf("\n");
+        }
+#endif
+
+        free(data);
+
+        count++;
+    }
+
+bail:
+    delete zip;
+    return count;
+}
+
+sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
+{
+    sp<AaptSymbols> sym = mSymbols.valueFor(name);
+    if (sym == NULL) {
+        sym = new AaptSymbols();
+        mSymbols.add(name, sym);
+    }
+    return sym;
+}
+
+status_t AaptAssets::buildIncludedResources(Bundle* bundle)
+{
+    if (!mHaveIncludedAssets) {
+        // Add in all includes.
+        const Vector<const char*>& incl = bundle->getPackageIncludes();
+        const size_t N=incl.size();
+        for (size_t i=0; i<N; i++) {
+            if (bundle->getVerbose())
+                printf("Including resources from package: %s\n", incl[i]);
+            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
+                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
+                        incl[i]);
+                return UNKNOWN_ERROR;
+            }
+        }
+        mHaveIncludedAssets = true;
+    }
+
+    return NO_ERROR;
+}
+
+status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
+{
+    const ResTable& res = getIncludedResources();
+    // XXX dirty!
+    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
+}
+
+const ResTable& AaptAssets::getIncludedResources() const
+{
+    return mIncludedAssets.getResources(false);
+}
+
+void AaptAssets::print() const
+{
+    printf("Locale/Vendor pairs:\n");
+    const size_t N=mGroupEntries.size();
+    for (size_t i=0; i<N; i++) {
+        printf("   %s/%s\n",
+               mGroupEntries.itemAt(i).locale.string(),
+               mGroupEntries.itemAt(i).vendor.string());
+    }
+
+    printf("\nFiles:\n");
+    AaptDir::print();
+}
+
+bool
+valid_symbol_name(const String8& symbol)
+{
+    static char const * const KEYWORDS[] = {
+        "abstract", "assert", "boolean", "break",
+        "byte", "case", "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends", "final",
+        "finally", "float", "for", "goto", "if", "implements", "import",
+        "instanceof", "int", "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short", "static",
+        "strictfp", "super", "switch", "synchronized", "this", "throw",
+        "throws", "transient", "try", "void", "volatile", "while",
+        "true", "false", "null",
+        NULL
+    };
+    const char*const* k = KEYWORDS;
+    const char*const s = symbol.string();
+    while (*k) {
+        if (0 == strcmp(s, *k)) {
+            return false;
+        }
+        k++;
+    }
+    return true;
+}
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
new file mode 100644
index 0000000..01c8140
--- /dev/null
+++ b/tools/aapt/AaptAssets.h
@@ -0,0 +1,519 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Information about assets being operated on.
+//
+#ifndef __AAPT_ASSETS_H
+#define __AAPT_ASSETS_H
+
+#include <stdlib.h>
+#include <utils/AssetManager.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/ResourceTypes.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/RefBase.h>
+#include <utils/ZipFile.h>
+
+#include "Bundle.h"
+#include "SourcePos.h"
+
+using namespace android;
+
+bool valid_symbol_name(const String8& str);
+
+enum {
+    AXIS_NONE = 0,
+    AXIS_MCC = 1,
+    AXIS_MNC,
+    AXIS_LANGUAGE,
+    AXIS_REGION,
+    AXIS_ORIENTATION,
+    AXIS_DENSITY,
+    AXIS_TOUCHSCREEN,
+    AXIS_KEYSHIDDEN,
+    AXIS_KEYBOARD,
+    AXIS_NAVIGATION,
+    AXIS_SCREENSIZE,
+    AXIS_VERSION
+};
+
+/**
+ * This structure contains a specific variation of a single file out
+ * of all the variations it can have that we can have.
+ */
+struct AaptGroupEntry
+{
+public:
+    AaptGroupEntry() { }
+    AaptGroupEntry(const String8& _locale, const String8& _vendor)
+        : locale(_locale), vendor(_vendor) { }
+
+    String8 mcc;
+    String8 mnc;
+    String8 locale;
+    String8 vendor;
+    String8 orientation;
+    String8 density;
+    String8 touchscreen;
+    String8 keysHidden;
+    String8 keyboard;
+    String8 navigation;
+    String8 screenSize;
+    String8 version;
+
+    bool initFromDirName(const char* dir, String8* resType);
+
+    static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
+    
+    static bool getMccName(const char* name, ResTable_config* out = NULL);
+    static bool getMncName(const char* name, ResTable_config* out = NULL);
+    static bool getLocaleName(const char* name, ResTable_config* out = NULL);
+    static bool getOrientationName(const char* name, ResTable_config* out = NULL);
+    static bool getDensityName(const char* name, ResTable_config* out = NULL);
+    static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
+    static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
+    static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
+    static bool getNavigationName(const char* name, ResTable_config* out = NULL);
+    static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
+    static bool getVersionName(const char* name, ResTable_config* out = NULL);
+
+    int compare(const AaptGroupEntry& o) const;
+
+    ResTable_config toParams() const;
+
+    inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
+    inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
+    inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
+    inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
+    inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
+    inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
+
+    String8 toString() const;
+    String8 toDirName(const String8& resType) const;
+};
+
+inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
+{
+    return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
+{
+    return compare_type(lhs, rhs) < 0;
+}
+
+class AaptGroup;
+
+/**
+ * A single asset file we know about.
+ */
+class AaptFile : public RefBase
+{
+public:
+    AaptFile(const String8& sourceFile, const AaptGroupEntry& groupEntry,
+             const String8& resType)
+        : mGroupEntry(groupEntry)
+        , mResourceType(resType)
+        , mSourceFile(sourceFile)
+        , mData(NULL)
+        , mDataSize(0)
+        , mBufferSize(0)
+        , mCompression(ZipEntry::kCompressStored)
+        {
+            //printf("new AaptFile created %s\n", (const char*)sourceFile);
+        }
+    virtual ~AaptFile() { }
+
+    const String8& getPath() const { return mPath; }
+    const AaptGroupEntry& getGroupEntry() const { return mGroupEntry; }
+
+    // Data API.  If there is data attached to the file,
+    // getSourceFile() is not used.
+    bool hasData() const { return mData != NULL; }
+    const void* getData() const { return mData; }
+    size_t getSize() const { return mDataSize; }
+    void* editData(size_t size);
+    void* editData(size_t* outSize = NULL);
+    void* padData(size_t wordSize);
+    status_t writeData(const void* data, size_t size);
+    void clearData();
+
+    const String8& getResourceType() const { return mResourceType; }
+
+    // File API.  If the file does not hold raw data, this is
+    // a full path to a file on the filesystem that holds its data.
+    const String8& getSourceFile() const { return mSourceFile; }
+
+    String8 getPrintableSource() const;
+
+    // Desired compression method, as per utils/ZipEntry.h.  For example,
+    // no compression is ZipEntry::kCompressStored.
+    int getCompressionMethod() const { return mCompression; }
+    void setCompressionMethod(int c) { mCompression = c; }
+private:
+    friend class AaptGroup;
+
+    String8 mPath;
+    AaptGroupEntry mGroupEntry;
+    String8 mResourceType;
+    String8 mSourceFile;
+    void* mData;
+    size_t mDataSize;
+    size_t mBufferSize;
+    int mCompression;
+};
+
+/**
+ * A group of related files (the same file, with different
+ * vendor/locale variations).
+ */
+class AaptGroup : public RefBase
+{
+public:
+    AaptGroup(const String8& leaf, const String8& path)
+        : mLeaf(leaf), mPath(path) { }
+    virtual ~AaptGroup() { }
+
+    const String8& getLeaf() const { return mLeaf; }
+
+    // Returns the relative path after the AaptGroupEntry dirs.
+    const String8& getPath() const { return mPath; }
+
+    const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& getFiles() const
+        { return mFiles; }
+
+    status_t addFile(const sp<AaptFile>& file);
+    void removeFile(size_t index);
+
+    void print() const;
+
+    String8 getPrintableSource() const;
+
+private:
+    String8 mLeaf;
+    String8 mPath;
+
+    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > mFiles;
+};
+
+/**
+ * A single directory of assets, which can contain for files and other
+ * sub-directories.
+ */
+class AaptDir : public RefBase
+{
+public:
+    AaptDir(const String8& leaf, const String8& path)
+        : mLeaf(leaf), mPath(path) { }
+    virtual ~AaptDir() { }
+
+    const String8& getLeaf() const { return mLeaf; }
+
+    const String8& getPath() const { return mPath; }
+
+    const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
+    const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
+
+    status_t addFile(const String8& name, const sp<AaptGroup>& file);
+    status_t addDir(const String8& name, const sp<AaptDir>& dir);
+
+    sp<AaptDir> makeDir(const String8& name);
+
+    void removeFile(const String8& name);
+    void removeDir(const String8& name);
+
+    status_t renameFile(const sp<AaptFile>& file, const String8& newName);
+
+    status_t addLeafFile(const String8& leafName,
+                         const sp<AaptFile>& file);
+
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType);
+
+    /*
+     * Perform some sanity checks on the names of files and directories here.
+     * In particular:
+     *  - Check for illegal chars in filenames.
+     *  - Check filename length.
+     *  - Check for presence of ".gz" and non-".gz" copies of same file.
+     *  - Check for multiple files whose names match in a case-insensitive
+     *    fashion (problematic for some systems).
+     *
+     * Comparing names against all other names is O(n^2).  We could speed
+     * it up some by sorting the entries and being smarter about what we
+     * compare against, but I'm not expecting to have enough files in a
+     * single directory to make a noticeable difference in speed.
+     *
+     * Note that sorting here is not enough to guarantee that the package
+     * contents are sorted -- subsequent updates can rearrange things.
+     */
+    status_t validate() const;
+
+    void print() const;
+
+    String8 getPrintableSource() const;
+
+private:
+    String8 mLeaf;
+    String8 mPath;
+
+    DefaultKeyedVector<String8, sp<AaptGroup> > mFiles;
+    DefaultKeyedVector<String8, sp<AaptDir> > mDirs;
+};
+
+/**
+ * All information we know about a particular symbol.
+ */
+class AaptSymbolEntry
+{
+public:
+    AaptSymbolEntry()
+        : isPublic(false), typeCode(TYPE_UNKNOWN)
+    {
+    }
+    AaptSymbolEntry(const String8& _name)
+        : name(_name), isPublic(false), typeCode(TYPE_UNKNOWN)
+    {
+    }
+    AaptSymbolEntry(const AaptSymbolEntry& o)
+        : name(o.name), sourcePos(o.sourcePos), isPublic(o.isPublic)
+        , comment(o.comment), typeComment(o.typeComment)
+        , typeCode(o.typeCode), int32Val(o.int32Val), stringVal(o.stringVal)
+    {
+    }
+    AaptSymbolEntry operator=(const AaptSymbolEntry& o)
+    {
+        sourcePos = o.sourcePos;
+        isPublic = o.isPublic;
+        comment = o.comment;
+        typeComment = o.typeComment;
+        typeCode = o.typeCode;
+        int32Val = o.int32Val;
+        stringVal = o.stringVal;
+        return *this;
+    }
+    
+    const String8 name;
+    
+    SourcePos sourcePos;
+    bool isPublic;
+    
+    String16 comment;
+    String16 typeComment;
+    
+    enum {
+        TYPE_UNKNOWN = 0,
+        TYPE_INT32,
+        TYPE_STRING
+    };
+    
+    int typeCode;
+    
+    // Value.  May be one of these.
+    int32_t int32Val;
+    String8 stringVal;
+};
+
+/**
+ * A group of related symbols (such as indices into a string block)
+ * that have been generated from the assets.
+ */
+class AaptSymbols : public RefBase
+{
+public:
+    AaptSymbols() { }
+    virtual ~AaptSymbols() { }
+
+    status_t addSymbol(const String8& name, int32_t value, const SourcePos& pos) {
+        if (!check_valid_symbol_name(name, pos, "symbol")) {
+            return BAD_VALUE;
+        }
+        AaptSymbolEntry& sym = edit_symbol(name, &pos);
+        sym.typeCode = AaptSymbolEntry::TYPE_INT32;
+        sym.int32Val = value;
+        return NO_ERROR;
+    }
+
+    status_t addStringSymbol(const String8& name, const String8& value,
+            const SourcePos& pos) {
+        if (!check_valid_symbol_name(name, pos, "symbol")) {
+            return BAD_VALUE;
+        }
+        AaptSymbolEntry& sym = edit_symbol(name, &pos);
+        sym.typeCode = AaptSymbolEntry::TYPE_STRING;
+        sym.stringVal = value;
+        return NO_ERROR;
+    }
+
+    status_t makeSymbolPublic(const String8& name, const SourcePos& pos) {
+        if (!check_valid_symbol_name(name, pos, "symbol")) {
+            return BAD_VALUE;
+        }
+        AaptSymbolEntry& sym = edit_symbol(name, &pos);
+        sym.isPublic = true;
+        return NO_ERROR;
+    }
+
+    void appendComment(const String8& name, const String16& comment, const SourcePos& pos) {
+        if (comment.size() <= 0) {
+            return;
+        }
+        AaptSymbolEntry& sym = edit_symbol(name, &pos);
+        if (sym.comment.size() == 0) {
+            sym.comment = comment;
+        } else {
+            sym.comment.append(String16("\n"));
+            sym.comment.append(comment);
+        }
+    }
+
+    void appendTypeComment(const String8& name, const String16& comment) {
+        if (comment.size() <= 0) {
+            return;
+        }
+        AaptSymbolEntry& sym = edit_symbol(name, NULL);
+        if (sym.typeComment.size() == 0) {
+            sym.typeComment = comment;
+        } else {
+            sym.typeComment.append(String16("\n"));
+            sym.typeComment.append(comment);
+        }
+    }
+    
+    sp<AaptSymbols> addNestedSymbol(const String8& name, const SourcePos& pos) {
+        if (!check_valid_symbol_name(name, pos, "nested symbol")) {
+            return NULL;
+        }
+        
+        sp<AaptSymbols> sym = mNestedSymbols.valueFor(name);
+        if (sym == NULL) {
+            sym = new AaptSymbols();
+            mNestedSymbols.add(name, sym);
+        }
+
+        return sym;
+    }
+
+    const KeyedVector<String8, AaptSymbolEntry>& getSymbols() const
+        { return mSymbols; }
+    const DefaultKeyedVector<String8, sp<AaptSymbols> >& getNestedSymbols() const
+        { return mNestedSymbols; }
+
+    const String16& getComment(const String8& name) const
+        { return get_symbol(name).comment; }
+    const String16& getTypeComment(const String8& name) const
+        { return get_symbol(name).typeComment; }
+
+private:
+    bool check_valid_symbol_name(const String8& symbol, const SourcePos& pos, const char* label) {
+        if (valid_symbol_name(symbol)) {
+            return true;
+        }
+        pos.error("invalid %s: '%s'\n", label, symbol.string());
+        return false;
+    }
+    AaptSymbolEntry& edit_symbol(const String8& symbol, const SourcePos* pos) {
+        ssize_t i = mSymbols.indexOfKey(symbol);
+        if (i < 0) {
+            i = mSymbols.add(symbol, AaptSymbolEntry(symbol));
+        }
+        AaptSymbolEntry& sym = mSymbols.editValueAt(i);
+        if (pos != NULL && sym.sourcePos.line < 0) {
+            sym.sourcePos = *pos;
+        }
+        return sym;
+    }
+    const AaptSymbolEntry& get_symbol(const String8& symbol) const {
+        ssize_t i = mSymbols.indexOfKey(symbol);
+        if (i >= 0) {
+            return mSymbols.valueAt(i);
+        }
+        return mDefSymbol;
+    }
+
+    KeyedVector<String8, AaptSymbolEntry>           mSymbols;
+    DefaultKeyedVector<String8, sp<AaptSymbols> >   mNestedSymbols;
+    AaptSymbolEntry                                 mDefSymbol;
+};
+
+class ResourceTypeSet;
+
+/**
+ * Asset hierarchy being operated on.
+ */
+class AaptAssets : public AaptDir
+{
+public:
+    AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false) { }
+    virtual ~AaptAssets() { }
+
+    const String8& getPackage() const { return mPackage; }
+    void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
+
+    const SortedVector<AaptGroupEntry>& getGroupEntries() const { return mGroupEntries; }
+
+    sp<AaptFile> addFile(const String8& filePath,
+                         const AaptGroupEntry& entry,
+                         const String8& srcDir,
+                         sp<AaptGroup>* outGroup,
+                         const String8& resType);
+
+    void addResource(const String8& leafName,
+                     const String8& path,
+                     const sp<AaptFile>& file,
+                     const String8& resType);
+
+    ssize_t slurpFromArgs(Bundle* bundle);
+
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType);
+
+    ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
+    ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
+
+    sp<AaptSymbols> getSymbolsFor(const String8& name);
+
+    const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
+
+    String8 getSymbolsPrivatePackage() const { return mSymbolsPrivatePackage; }
+    void setSymbolsPrivatePackage(const String8& pkg) { mSymbolsPrivatePackage = pkg; }
+    
+    status_t buildIncludedResources(Bundle* bundle);
+    status_t addIncludedResources(const sp<AaptFile>& file);
+    const ResTable& getIncludedResources() const;
+
+    void print() const;
+
+    inline const Vector<sp<AaptDir> >& resDirs() { return mDirs; }
+
+    inline sp<AaptAssets> getOverlay() { return mOverlay; }
+    inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
+    
+    inline KeyedVector<String8, sp<ResourceTypeSet> >* getResources() { return mRes; }
+    inline void 
+        setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { mRes = res; }
+
+private:
+    String8 mPackage;
+    SortedVector<AaptGroupEntry> mGroupEntries;
+    DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
+    String8 mSymbolsPrivatePackage;
+
+    Vector<sp<AaptDir> > mDirs;
+
+    bool mHaveIncludedAssets;
+    AssetManager mIncludedAssets;
+
+    sp<AaptAssets> mOverlay;
+    KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
+};
+
+#endif // __AAPT_ASSETS_H
+
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
new file mode 100644
index 0000000..fdc859c
--- /dev/null
+++ b/tools/aapt/Android.mk
@@ -0,0 +1,52 @@
+# 
+# Copyright 2006 The Android Open Source Project
+#
+# Android Asset Packaging Tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	AaptAssets.cpp \
+	Command.cpp \
+	Main.cpp \
+	Package.cpp \
+	StringPool.cpp \
+	XMLNode.cpp \
+	ResourceTable.cpp \
+	Images.cpp \
+	Resource.cpp \
+    SourcePos.cpp
+
+LOCAL_CFLAGS += -Wno-format-y2k
+
+LOCAL_C_INCLUDES += external/expat/lib
+LOCAL_C_INCLUDES += external/libpng
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_C_INCLUDES += build/libs/host/include
+
+#LOCAL_WHOLE_STATIC_LIBRARIES := 
+LOCAL_STATIC_LIBRARIES := \
+	libhost \
+	libutils \
+	libcutils \
+	libexpat \
+	libpng
+
+LOCAL_LDLIBS := -lz
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lrt
+endif
+
+ifeq ($(HOST_OS),windows)
+ifeq ($(strip $(USE_CYGWIN),),)
+LOCAL_LDLIBS += -lws2_32
+endif
+endif
+
+LOCAL_MODULE := aapt
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
new file mode 100644
index 0000000..2d8471b
--- /dev/null
+++ b/tools/aapt/Bundle.h
@@ -0,0 +1,164 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// State bundle.  Used to pass around stuff like command-line args.
+//
+#ifndef __BUNDLE_H
+#define __BUNDLE_H
+
+#include <stdlib.h>
+#include <utils.h>      // android
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+/*
+ * Things we can do.
+ */
+typedef enum Command {
+    kCommandUnknown = 0,
+    kCommandVersion,
+    kCommandList,
+    kCommandDump,
+    kCommandAdd,
+    kCommandRemove,
+    kCommandPackage,
+} Command;
+
+/*
+ * Bundle of goodies, including everything specified on the command line.
+ */
+class Bundle {
+public:
+    Bundle(void)
+        : mCmd(kCommandUnknown), mVerbose(false), mAndroidList(false),
+          mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
+          mUpdate(false), mExtending(false),
+          mRequireLocalization(false), mPseudolocalize(false),
+          mCompressionMethod(0), mOutputAPKFile(NULL),
+          mAssetSourceDir(NULL),
+          mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
+          mRClassDir(NULL), mResourceIntermediatesDir(NULL),
+          mArgc(0), mArgv(NULL)
+        {}
+    ~Bundle(void) {}
+
+    /*
+     * Set the command value.  Returns "false" if it was previously set.
+     */
+    Command getCommand(void) const { return mCmd; }
+    void setCommand(Command cmd) { mCmd = cmd; }
+
+    /*
+     * Command modifiers.  Not all modifiers are appropriate for all
+     * commands.
+     */
+    bool getVerbose(void) const { return mVerbose; }
+    void setVerbose(bool val) { mVerbose = val; }
+    bool getAndroidList(void) const { return mAndroidList; }
+    void setAndroidList(bool val) { mAndroidList = val; }
+    bool getForce(void) const { return mForce; }
+    void setForce(bool val) { mForce = val; }
+    void setGrayscaleTolerance(int val) { mGrayscaleTolerance = val; }
+    int  getGrayscaleTolerance() { return mGrayscaleTolerance; }
+    bool getMakePackageDirs(void) const { return mMakePackageDirs; }
+    void setMakePackageDirs(bool val) { mMakePackageDirs = val; }
+    bool getUpdate(void) const { return mUpdate; }
+    void setUpdate(bool val) { mUpdate = val; }
+    bool getExtending(void) const { return mExtending; }
+    void setExtending(bool val) { mExtending = val; }
+    bool getRequireLocalization(void) const { return mRequireLocalization; }
+    void setRequireLocalization(bool val) { mRequireLocalization = val; }
+    bool getPseudolocalize(void) const { return mPseudolocalize; }
+    void setPseudolocalize(bool val) { mPseudolocalize = val; }
+    int getCompressionMethod(void) const { return mCompressionMethod; }
+    void setCompressionMethod(int val) { mCompressionMethod = val; }
+    const char* getOutputAPKFile() const { return mOutputAPKFile; }
+    void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
+
+    /*                                                                
+     * Input options.                                                                
+     */
+    const char* getAssetSourceDir() const { return mAssetSourceDir; }
+    void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; }
+    const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
+    void addResourceSourceDir(const char* dir) { mResourceSourceDirs.insertAt(dir,0); }
+    const char* getAndroidManifestFile() const { return mAndroidManifestFile; }
+    void setAndroidManifestFile(const char* file) { mAndroidManifestFile = file; }
+    const char* getPublicOutputFile() const { return mPublicOutputFile; }
+    void setPublicOutputFile(const char* file) { mPublicOutputFile = file; }
+    const char* getRClassDir() const { return mRClassDir; }
+    void setRClassDir(const char* dir) { mRClassDir = dir; }
+    const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
+    void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
+    const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
+    void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
+    const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
+    void addPackageInclude(const char* file) { mPackageIncludes.add(file); }
+    const android::Vector<const char*>& getJarFiles() const { return mJarFiles; }
+    void addJarFile(const char* file) { mJarFiles.add(file); }
+    const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; }
+    void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
+
+    /*
+     * Set and get the file specification.
+     *
+     * Note this does NOT make a copy of argv.
+     */
+    void setFileSpec(char* const argv[], int argc) {
+        mArgc = argc;
+        mArgv = argv;
+    }
+    int getFileSpecCount(void) const { return mArgc; }
+    const char* getFileSpecEntry(int idx) const { return mArgv[idx]; }
+    void eatArgs(int n) {
+        if (n > mArgc) n = mArgc;
+        mArgv += n;
+        mArgc -= n;
+    }
+
+#if 0
+    /*
+     * Package count.  Nothing to do with anything else here; this is
+     * just a convenient place to stuff it so we don't have to pass it
+     * around everywhere.
+     */
+    int getPackageCount(void) const { return mPackageCount; }
+    void setPackageCount(int val) { mPackageCount = val; }
+#endif
+
+private:
+    /* commands & modifiers */
+    Command     mCmd;
+    bool        mVerbose;
+    bool        mAndroidList;
+    bool        mForce;
+    int         mGrayscaleTolerance;
+    bool        mMakePackageDirs;
+    bool        mUpdate;
+    bool        mExtending;
+    bool        mRequireLocalization;
+    bool        mPseudolocalize;
+    int         mCompressionMethod;
+    const char* mOutputAPKFile;
+    const char* mAssetSourceDir;
+    const char* mAndroidManifestFile;
+    const char* mPublicOutputFile;
+    const char* mRClassDir;
+    const char* mResourceIntermediatesDir;
+    android::String8 mConfigurations;
+    android::Vector<const char*> mPackageIncludes;
+    android::Vector<const char*> mJarFiles;
+    android::Vector<const char*> mNoCompressExtensions;
+    android::Vector<const char*> mResourceSourceDirs;
+    
+    /* file specification */
+    int         mArgc;
+    char* const* mArgv;
+
+#if 0
+    /* misc stuff */
+    int         mPackageCount;
+#endif
+};
+
+#endif // __BUNDLE_H
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
new file mode 100644
index 0000000..bff0423
--- /dev/null
+++ b/tools/aapt/Command.cpp
@@ -0,0 +1,841 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Android Asset Packaging Tool main entry point.
+//
+#include "Main.h"
+#include "Bundle.h"
+#include "ResourceTable.h"
+#include "XMLNode.h"
+
+#include <utils.h>
+#include <utils/ZipFile.h>
+
+#include <fcntl.h>
+#include <errno.h>
+
+using namespace android;
+
+/*
+ * Show version info.  All the cool kids do it.
+ */
+int doVersion(Bundle* bundle)
+{
+    if (bundle->getFileSpecCount() != 0)
+        printf("(ignoring extra arguments)\n");
+    printf("Android Asset Packaging Tool, v0.2\n");
+
+    return 0;
+}
+
+
+/*
+ * Open the file read only.  The call fails if the file doesn't exist.
+ *
+ * Returns NULL on failure.
+ */
+ZipFile* openReadOnly(const char* fileName)
+{
+    ZipFile* zip;
+    status_t result;
+
+    zip = new ZipFile;
+    result = zip->open(fileName, ZipFile::kOpenReadOnly);
+    if (result != NO_ERROR) {
+        if (result == NAME_NOT_FOUND)
+            fprintf(stderr, "ERROR: '%s' not found\n", fileName);
+        else if (result == PERMISSION_DENIED)
+            fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
+        else
+            fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
+                fileName);
+        delete zip;
+        return NULL;
+    }
+
+    return zip;
+}
+
+/*
+ * Open the file read-write.  The file will be created if it doesn't
+ * already exist and "okayToCreate" is set.
+ *
+ * Returns NULL on failure.
+ */
+ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
+{
+    ZipFile* zip = NULL;
+    status_t result;
+    int flags;
+
+    flags = ZipFile::kOpenReadWrite;
+    if (okayToCreate)
+        flags |= ZipFile::kOpenCreate;
+
+    zip = new ZipFile;
+    result = zip->open(fileName, flags);
+    if (result != NO_ERROR) {
+        delete zip;
+        zip = NULL;
+        goto bail;
+    }
+
+bail:
+    return zip;
+}
+
+
+/*
+ * Return a short string describing the compression method.
+ */
+const char* compressionName(int method)
+{
+    if (method == ZipEntry::kCompressStored)
+        return "Stored";
+    else if (method == ZipEntry::kCompressDeflated)
+        return "Deflated";
+    else
+        return "Unknown";
+}
+
+/*
+ * Return the percent reduction in size (0% == no compression).
+ */
+int calcPercent(long uncompressedLen, long compressedLen)
+{
+    if (!uncompressedLen)
+        return 0;
+    else
+        return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
+}
+
+/*
+ * Handle the "list" command, which can be a simple file dump or
+ * a verbose listing.
+ *
+ * The verbose listing closely matches the output of the Info-ZIP "unzip"
+ * command.
+ */
+int doList(Bundle* bundle)
+{
+    int result = 1;
+    ZipFile* zip = NULL;
+    const ZipEntry* entry;
+    long totalUncLen, totalCompLen;
+    const char* zipFileName;
+
+    if (bundle->getFileSpecCount() != 1) {
+        fprintf(stderr, "ERROR: specify zip file name (only)\n");
+        goto bail;
+    }
+    zipFileName = bundle->getFileSpecEntry(0);
+
+    zip = openReadOnly(zipFileName);
+    if (zip == NULL)
+        goto bail;
+
+    int count, i;
+
+    if (bundle->getVerbose()) {
+        printf("Archive:  %s\n", zipFileName);
+        printf(
+            " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n");
+        printf(
+            "--------  ------  ------- -----   ----   ----   ------    ----\n");
+    }
+
+    totalUncLen = totalCompLen = 0;
+
+    count = zip->getNumEntries();
+    for (i = 0; i < count; i++) {
+        entry = zip->getEntryByIndex(i);
+        if (bundle->getVerbose()) {
+            char dateBuf[32];
+            time_t when;
+
+            when = entry->getModWhen();
+            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
+                localtime(&when));
+
+            printf("%8ld  %-7.7s %7ld %3d%%  %s  %08lx  %s\n",
+                (long) entry->getUncompressedLen(),
+                compressionName(entry->getCompressionMethod()),
+                (long) entry->getCompressedLen(),
+                calcPercent(entry->getUncompressedLen(),
+                            entry->getCompressedLen()),
+                dateBuf,
+                entry->getCRC32(),
+                entry->getFileName());
+        } else {
+            printf("%s\n", entry->getFileName());
+        }
+
+        totalUncLen += entry->getUncompressedLen();
+        totalCompLen += entry->getCompressedLen();
+    }
+
+    if (bundle->getVerbose()) {
+        printf(
+        "--------          -------  ---                            -------\n");
+        printf("%8ld          %7ld  %2d%%                            %d files\n",
+            totalUncLen,
+            totalCompLen,
+            calcPercent(totalUncLen, totalCompLen),
+            zip->getNumEntries());
+    }
+
+    if (bundle->getAndroidList()) {
+        AssetManager assets;
+        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
+            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
+            goto bail;
+        }
+        
+        const ResTable& res = assets.getResources(false);
+        if (&res == NULL) {
+            printf("\nNo resource table found.\n");
+        } else {
+            printf("\nResource table:\n");
+            res.print();
+        }
+        
+        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
+                                                   Asset::ACCESS_BUFFER);
+        if (manifestAsset == NULL) {
+            printf("\nNo AndroidManifest.xml found.\n");
+        } else {
+            printf("\nAndroid manifest:\n");
+            ResXMLTree tree;
+            tree.setTo(manifestAsset->getBuffer(true),
+                       manifestAsset->getLength());
+            printXMLBlock(&tree);
+        }
+        delete manifestAsset;
+    }
+    
+    result = 0;
+
+bail:
+    delete zip;
+    return result;
+}
+
+static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
+{
+    size_t N = tree.getAttributeCount();
+    for (size_t i=0; i<N; i++) {
+        if (tree.getAttributeNameResID(i) == attrRes) {
+            return (ssize_t)i;
+        }
+    }
+    return -1;
+}
+
+static String8 getAttribute(const ResXMLTree& tree, const char* ns,
+                            const char* attr, String8* outError)
+{
+    ssize_t idx = tree.indexOfAttribute(ns, attr);
+    if (idx < 0) {
+        return String8();
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType != Res_value::TYPE_STRING) {
+            if (outError != NULL) *outError = "attribute is not a string value";
+            return String8();
+        }
+    }
+    size_t len;
+    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+    return str ? String8(str, len) : String8();
+}
+
+static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
+{
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType != Res_value::TYPE_STRING) {
+            if (outError != NULL) *outError = "attribute is not a string value";
+            return String8();
+        }
+    }
+    size_t len;
+    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+    return str ? String8(str, len) : String8();
+}
+
+static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
+{
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return -1;
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType != Res_value::TYPE_INT_DEC) {
+            if (outError != NULL) *outError = "attribute is not an integer value";
+            return -1;
+        }
+    }
+    return value.data;
+}
+
+static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
+        uint32_t attrRes, String8* outError)
+{
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_STRING) {
+            size_t len;
+            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+            return str ? String8(str, len) : String8();
+        }
+        resTable->resolveReference(&value, 0);
+        if (value.dataType != Res_value::TYPE_STRING) {
+            if (outError != NULL) *outError = "attribute is not a string value";
+            return String8();
+        }
+    }
+    size_t len;
+    const Res_value* value2 = &value;
+    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
+    return str ? String8(str, len) : String8();
+}
+
+// These are attribute resource constants for the platform, as found
+// in android.R.attr
+enum {
+    NAME_ATTR = 0x01010003,
+    VERSION_CODE_ATTR = 0x0101021b,
+    VERSION_NAME_ATTR = 0x0101021c,
+    LABEL_ATTR = 0x01010001,
+    ICON_ATTR = 0x01010002,
+};
+
+/*
+ * Handle the "dump" command, to extract select data from an archive.
+ */
+int doDump(Bundle* bundle)
+{
+    status_t result = UNKNOWN_ERROR;
+    Asset* asset = NULL;
+    
+    if (bundle->getFileSpecCount() < 1) {
+        fprintf(stderr, "ERROR: no dump option specified\n");
+        return 1;
+    }
+    
+    if (bundle->getFileSpecCount() < 2) {
+        fprintf(stderr, "ERROR: no dump file specified\n");
+        return 1;
+    }
+    
+    const char* option = bundle->getFileSpecEntry(0);
+    const char* filename = bundle->getFileSpecEntry(1);
+    
+    AssetManager assets;
+    if (!assets.addAssetPath(String8(filename), NULL)) {
+        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
+        return 1;
+    }
+    
+    const ResTable& res = assets.getResources(false);
+    if (&res == NULL) {
+        fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
+        goto bail;
+    }
+    
+    if (strcmp("resources", option) == 0) {
+        res.print();
+        
+    } else if (strcmp("xmltree", option) == 0) {
+        if (bundle->getFileSpecCount() < 3) {
+            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
+            goto bail;
+        }
+    
+        for (int i=2; i<bundle->getFileSpecCount(); i++) {
+            const char* resname = bundle->getFileSpecEntry(i);
+            ResXMLTree tree;
+            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
+            if (asset == NULL) {
+                fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
+                goto bail;
+            }
+            
+            if (tree.setTo(asset->getBuffer(true),
+                           asset->getLength()) != NO_ERROR) {
+                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
+                goto bail;
+            }
+            tree.restart();
+            printXMLBlock(&tree);
+            delete asset;
+            asset = NULL;
+        }
+        
+    } else if (strcmp("xmlstrings", option) == 0) {
+        if (bundle->getFileSpecCount() < 3) {
+            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
+            goto bail;
+        }
+    
+        for (int i=2; i<bundle->getFileSpecCount(); i++) {
+            const char* resname = bundle->getFileSpecEntry(i);
+            ResXMLTree tree;
+            asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
+            if (asset == NULL) {
+                fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname);
+                goto bail;
+            }
+            
+            if (tree.setTo(asset->getBuffer(true),
+                           asset->getLength()) != NO_ERROR) {
+                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
+                goto bail;
+            }
+            printStringPool(&tree.getStrings());
+            delete asset;
+            asset = NULL;
+        }
+        
+    } else {
+        ResXMLTree tree;
+        asset = assets.openNonAsset("AndroidManifest.xml",
+                                            Asset::ACCESS_BUFFER);
+        if (asset == NULL) {
+            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
+            goto bail;
+        }
+        
+        if (tree.setTo(asset->getBuffer(true),
+                       asset->getLength()) != NO_ERROR) {
+            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
+            goto bail;
+        }
+        tree.restart();
+        
+        if (strcmp("permissions", option) == 0) {
+            size_t len;
+            ResXMLTree::event_code_t code;
+            int depth = 0;
+            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                if (code == ResXMLTree::END_TAG) {
+                    depth--;
+                    continue;
+                }
+                if (code != ResXMLTree::START_TAG) {
+                    continue;
+                }
+                depth++;
+                String8 tag(tree.getElementName(&len));
+                //printf("Depth %d tag %s\n", depth, tag.string());
+                if (depth == 1) {
+                    if (tag != "manifest") {
+                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
+                        goto bail;
+                    }
+                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
+                    printf("package: %s\n", pkg.string());
+                } else if (depth == 2 && tag == "permission") {
+                    String8 error;
+                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR: %s\n", error.string());
+                        goto bail;
+                    }
+                    printf("permission: %s\n", name.string());
+                } else if (depth == 2 && tag == "uses-permission") {
+                    String8 error;
+                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR: %s\n", error.string());
+                        goto bail;
+                    }
+                    printf("uses-permission: %s\n", name.string());
+                }
+            }
+        } else if (strcmp("badging", option) == 0) {
+            size_t len;
+            ResXMLTree::event_code_t code;
+            int depth = 0;
+            String8 error;
+            bool withinActivity = false;
+            bool isMainActivity = false;
+            bool isLauncherActivity = false;
+            String8 activityName;
+            String8 activityLabel;
+            String8 activityIcon;
+            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                if (code == ResXMLTree::END_TAG) {
+                    depth--;
+                    continue;
+                }
+                if (code != ResXMLTree::START_TAG) {
+                    continue;
+                }
+                depth++;
+                String8 tag(tree.getElementName(&len));
+                //printf("Depth %d tag %s\n", depth, tag.string());
+                if (depth == 1) {
+                    if (tag != "manifest") {
+                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
+                        goto bail;
+                    }
+                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
+                    printf("package: name='%s' ", pkg.string());
+                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+                    if (versionCode > 0) {
+                        printf("versionCode='%d' ", versionCode);
+                    } else {
+                        printf("versionCode='' ");
+                    }
+                    String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+                    printf("versionName='%s'\n", versionName.string());
+                } else if (depth == 2 && tag == "application") {
+                    String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+                    printf("application: label='%s' ", label.string());
+
+                    String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+                    printf("icon='%s'\n", icon.string());
+                } else if (depth == 3 && tag == "activity") {
+                    withinActivity = true;
+                    //printf("LOG: withinActivity==true\n");
+
+                    activityName = getAttribute(tree, NAME_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+
+                    activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+
+                    activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+                        goto bail;
+                    }
+                } else if (depth == 5 && withinActivity) {
+                    if (tag == "action") {
+                        //printf("LOG: action tag\n");
+                        String8 action = getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
+                            goto bail;
+                        }
+                        if (action == "android.intent.action.MAIN") {
+                            isMainActivity = true;
+                            //printf("LOG: isMainActivity==true\n");
+                        }
+                    } else if (tag == "category") {
+                        String8 category = getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
+                            goto bail;
+                        }
+                        if (category == "android.intent.category.LAUNCHER") {
+                            isLauncherActivity = true;
+                            //printf("LOG: isLauncherActivity==true\n");
+                       }
+                    }
+                }
+
+                if (depth < 3) {
+                    //if (withinActivity) printf("LOG: withinActivity==false\n");
+                    withinActivity = false;
+                }
+
+                if (depth < 5) {
+                    //if (isMainActivity) printf("LOG: isMainActivity==false\n");
+                    //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n");
+                    isMainActivity = false;
+                    isLauncherActivity = false;
+                }
+
+                if (withinActivity && isMainActivity && isLauncherActivity) {
+                    printf("launchable activity: name='%s' label='%s' icon='%s'\n",
+                           activityName.string(), activityLabel.string(),
+                           activityIcon.string());
+                }
+            }
+            printf("locales:");
+            Vector<String8> locales;
+            res.getLocales(&locales);
+            const size_t N = locales.size();
+            for (size_t i=0; i<N; i++) {
+                const char* localeStr =  locales[i].string();
+                if (localeStr == NULL || strlen(localeStr) == 0) {
+                    localeStr = "--_--";
+                }
+                printf(" '%s'", localeStr);
+            }
+            printf("\n");
+        } else if (strcmp("configurations", option) == 0) {
+            Vector<ResTable_config> configs;
+            res.getConfigurations(&configs);
+            const size_t N = configs.size();
+            for (size_t i=0; i<N; i++) {
+                printf("%s\n", configs[i].toString().string());
+            }
+        } else {
+            fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
+            goto bail;
+        }
+    }
+
+    result = NO_ERROR;
+    
+bail:
+    if (asset) {
+        delete asset;
+    }
+    return (result != NO_ERROR);
+}
+
+
+/*
+ * Handle the "add" command, which wants to add files to a new or
+ * pre-existing archive.
+ */
+int doAdd(Bundle* bundle)
+{
+    ZipFile* zip = NULL;
+    status_t result = UNKNOWN_ERROR;
+    const char* zipFileName;
+
+    if (bundle->getUpdate()) {
+        /* avoid confusion */
+        fprintf(stderr, "ERROR: can't use '-u' with add\n");
+        goto bail;
+    }
+
+    if (bundle->getFileSpecCount() < 1) {
+        fprintf(stderr, "ERROR: must specify zip file name\n");
+        goto bail;
+    }
+    zipFileName = bundle->getFileSpecEntry(0);
+
+    if (bundle->getFileSpecCount() < 2) {
+        fprintf(stderr, "NOTE: nothing to do\n");
+        goto bail;
+    }
+
+    zip = openReadWrite(zipFileName, true);
+    if (zip == NULL) {
+        fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
+        goto bail;
+    }
+
+    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
+        const char* fileName = bundle->getFileSpecEntry(i);
+
+        if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
+            printf(" '%s'... (from gzip)\n", fileName);
+            result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
+        } else {
+            printf(" '%s'...\n", fileName);
+            result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
+        }
+        if (result != NO_ERROR) {
+            fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
+            if (result == NAME_NOT_FOUND)
+                fprintf(stderr, ": file not found\n");
+            else if (result == ALREADY_EXISTS)
+                fprintf(stderr, ": already exists in archive\n");
+            else
+                fprintf(stderr, "\n");
+            goto bail;
+        }
+    }
+
+    result = NO_ERROR;
+
+bail:
+    delete zip;
+    return (result != NO_ERROR);
+}
+
+
+/*
+ * Delete files from an existing archive.
+ */
+int doRemove(Bundle* bundle)
+{
+    ZipFile* zip = NULL;
+    status_t result = UNKNOWN_ERROR;
+    const char* zipFileName;
+
+    if (bundle->getFileSpecCount() < 1) {
+        fprintf(stderr, "ERROR: must specify zip file name\n");
+        goto bail;
+    }
+    zipFileName = bundle->getFileSpecEntry(0);
+
+    if (bundle->getFileSpecCount() < 2) {
+        fprintf(stderr, "NOTE: nothing to do\n");
+        goto bail;
+    }
+
+    zip = openReadWrite(zipFileName, false);
+    if (zip == NULL) {
+        fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
+            zipFileName);
+        goto bail;
+    }
+
+    for (int i = 1; i < bundle->getFileSpecCount(); i++) {
+        const char* fileName = bundle->getFileSpecEntry(i);
+        ZipEntry* entry;
+
+        entry = zip->getEntryByName(fileName);
+        if (entry == NULL) {
+            printf(" '%s' NOT FOUND\n", fileName);
+            continue;
+        }
+
+        result = zip->remove(entry);
+
+        if (result != NO_ERROR) {
+            fprintf(stderr, "Unable to delete '%s' from '%s'\n",
+                bundle->getFileSpecEntry(i), zipFileName);
+            goto bail;
+        }
+    }
+
+    /* update the archive */
+    zip->flush();
+
+bail:
+    delete zip;
+    return (result != NO_ERROR);
+}
+
+
+/*
+ * Package up an asset directory and associated application files.
+ */
+int doPackage(Bundle* bundle)
+{
+    const char* outputAPKFile;
+    int retVal = 1;
+    status_t err;
+    sp<AaptAssets> assets;
+    int N;
+
+    // -c zz_ZZ means do pseudolocalization
+    ResourceFilter filter;
+    err = filter.parse(bundle->getConfigurations());
+    if (err != NO_ERROR) {
+        goto bail;
+    }
+    if (filter.containsPseudo()) {
+        bundle->setPseudolocalize(true);
+    }
+
+    N = bundle->getFileSpecCount();
+    if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
+            && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
+        fprintf(stderr, "ERROR: no input files\n");
+        goto bail;
+    }
+
+    outputAPKFile = bundle->getOutputAPKFile();
+
+    // Make sure the filenames provided exist and are of the appropriate type.
+    if (outputAPKFile) {
+        FileType type;
+        type = getFileType(outputAPKFile);
+        if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
+            fprintf(stderr,
+                "ERROR: output file '%s' exists but is not regular file\n",
+                outputAPKFile);
+            goto bail;
+        }
+    }
+
+    // Load the assets.
+    assets = new AaptAssets();
+    err = assets->slurpFromArgs(bundle);
+    if (err < 0) {
+        goto bail;
+    }
+
+    if (bundle->getVerbose()) {
+        assets->print();
+    }
+
+    // If they asked for any files that need to be compiled, do so.
+    if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
+        err = buildResources(bundle, assets);
+        if (err != 0) {
+            goto bail;
+        }
+    }
+
+    // At this point we've read everything and processed everything.  From here
+    // on out it's just writing output files.
+    if (SourcePos::hasErrors()) {
+        goto bail;
+    }
+
+    // Write out R.java constants
+    if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
+        err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
+        if (err < 0) {
+            goto bail;
+        }
+    } else {
+        err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
+        if (err < 0) {
+            goto bail;
+        }
+        err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
+        if (err < 0) {
+            goto bail;
+        }
+    }
+
+    // Write the apk
+    if (outputAPKFile) {
+        err = writeAPK(bundle, assets, String8(outputAPKFile));
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
+            goto bail;
+        }
+    }
+
+    retVal = 0;
+bail:
+    if (SourcePos::hasErrors()) {
+        SourcePos::printErrors(stderr);
+    }
+    return retVal;
+}
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
new file mode 100644
index 0000000..edb12ea
--- /dev/null
+++ b/tools/aapt/Images.cpp
@@ -0,0 +1,1082 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#define PNG_INTERNAL
+
+#include "Images.h"
+
+#include <utils/ResourceTypes.h>
+#include <utils/ByteOrder.h>
+
+#include <png.h>
+
+#define NOISY(x) //x
+
+static void
+png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+    status_t err = ((AaptFile*)png_ptr->io_ptr)->writeData(data, length);
+    if (err != NO_ERROR) {
+        png_error(png_ptr, "Write Error");
+    }
+}
+
+
+static void
+png_flush_aapt_file(png_structp png_ptr)
+{
+}
+
+// This holds an image as 8bpp RGBA.
+struct image_info
+{
+    image_info() : rows(NULL), is9Patch(false), allocRows(NULL) { }
+    ~image_info() {
+        if (rows && rows != allocRows) {
+            free(rows);
+        }
+        if (allocRows) {
+            for (int i=0; i<(int)allocHeight; i++) {
+                free(allocRows[i]);
+            }
+            free(allocRows);
+        }
+    }
+
+    png_uint_32 width;
+    png_uint_32 height;
+    png_bytepp rows;
+
+    // 9-patch info.
+    bool is9Patch;
+    Res_png_9patch info9Patch;
+
+    png_uint_32 allocHeight;
+    png_bytepp allocRows;
+};
+
+static void read_png(const char* imageName,
+                     png_structp read_ptr, png_infop read_info,
+                     image_info* outImageInfo)
+{
+    int color_type;
+    int bit_depth, interlace_type, compression_type;
+    int i;
+
+    png_read_info(read_ptr, read_info);
+
+    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
+       &outImageInfo->height, &bit_depth, &color_type,
+       &interlace_type, &compression_type, NULL);
+
+    //printf("Image %s:\n", imageName);
+    //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
+    //       color_type, bit_depth, interlace_type, compression_type);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        png_set_palette_to_rgb(read_ptr);
+
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+        png_set_gray_1_2_4_to_8(read_ptr);
+
+    if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
+        //printf("Has PNG_INFO_tRNS!\n");
+        png_set_tRNS_to_alpha(read_ptr);
+    }
+
+    if (bit_depth == 16)
+        png_set_strip_16(read_ptr);
+
+    if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
+        png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
+
+    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(read_ptr);
+
+    png_read_update_info(read_ptr, read_info);
+
+    outImageInfo->rows = (png_bytepp)malloc(
+        outImageInfo->height * png_sizeof(png_bytep));
+    outImageInfo->allocHeight = outImageInfo->height;
+    outImageInfo->allocRows = outImageInfo->rows;
+
+    png_set_rows(read_ptr, read_info, outImageInfo->rows);
+
+    for (i = 0; i < (int)outImageInfo->height; i++)
+    {
+        outImageInfo->rows[i] = (png_bytep)
+            malloc(png_get_rowbytes(read_ptr, read_info));
+    }
+
+    png_read_image(read_ptr, outImageInfo->rows);
+
+    png_read_end(read_ptr, read_info);
+
+    NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
+                 imageName,
+                 (int)outImageInfo->width, (int)outImageInfo->height,
+                 bit_depth, color_type,
+                 interlace_type, compression_type));
+
+    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
+       &outImageInfo->height, &bit_depth, &color_type,
+       &interlace_type, &compression_type, NULL);
+}
+
+static bool is_tick(png_bytep p, bool transparent, const char** outError)
+{
+    if (transparent) {
+        if (p[3] == 0) {
+            return false;
+        }
+        if (p[3] != 0xff) {
+            *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
+            return false;
+        }
+        if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+            *outError = "Ticks in transparent frame must be black";
+        }
+        return true;
+    }
+
+    if (p[3] != 0xFF) {
+        *outError = "White frame must be a solid color (no alpha)";
+    }
+    if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
+        return false;
+    }
+    if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+        *outError = "Ticks in white frame must be black";
+        return false;
+    }
+    return true;
+}
+
+enum {
+    TICK_START,
+    TICK_INSIDE_1,
+    TICK_OUTSIDE_1
+};
+
+static status_t get_horizontal_ticks(
+        png_bytep row, int width, bool transparent, bool required,
+        int32_t* outLeft, int32_t* outRight, const char** outError,
+        uint8_t* outDivs, bool multipleAllowed)
+{
+    int i;
+    *outLeft = *outRight = -1;
+    int state = TICK_START;
+    bool found = false;
+
+    for (i=1; i<width-1; i++) {
+        if (is_tick(row+i*4, transparent, outError)) {
+            if (state == TICK_START ||
+                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
+                *outLeft = i-1;
+                *outRight = width-2;
+                found = true;
+                if (outDivs != NULL) {
+                    *outDivs += 2;
+                }
+                state = TICK_INSIDE_1;
+            } else if (state == TICK_OUTSIDE_1) {
+                *outError = "Can't have more than one marked region along edge";
+                *outLeft = i;
+                return UNKNOWN_ERROR;
+            }
+        } else if (*outError == NULL) {
+            if (state == TICK_INSIDE_1) {
+                // We're done with this div.  Move on to the next.
+                *outRight = i-1;
+                outRight += 2;
+                outLeft += 2;
+                state = TICK_OUTSIDE_1;
+            }
+        } else {
+            *outLeft = i;
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (required && !found) {
+        *outError = "No marked region found along edge";
+        *outLeft = -1;
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+static status_t get_vertical_ticks(
+        png_bytepp rows, int offset, int height, bool transparent, bool required,
+        int32_t* outTop, int32_t* outBottom, const char** outError,
+        uint8_t* outDivs, bool multipleAllowed)
+{
+    int i;
+    *outTop = *outBottom = -1;
+    int state = TICK_START;
+    bool found = false;
+
+    for (i=1; i<height-1; i++) {
+        if (is_tick(rows[i]+offset, transparent, outError)) {
+            if (state == TICK_START ||
+                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
+                *outTop = i-1;
+                *outBottom = height-2;
+                found = true;
+                if (outDivs != NULL) {
+                    *outDivs += 2;
+                }
+                state = TICK_INSIDE_1;
+            } else if (state == TICK_OUTSIDE_1) {
+                *outError = "Can't have more than one marked region along edge";
+                *outTop = i;
+                return UNKNOWN_ERROR;
+            }
+        } else if (*outError == NULL) {
+            if (state == TICK_INSIDE_1) {
+                // We're done with this div.  Move on to the next.
+                *outBottom = i-1;
+                outTop += 2;
+                outBottom += 2;
+                state = TICK_OUTSIDE_1;
+            }
+        } else {
+            *outTop = i;
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (required && !found) {
+        *outError = "No marked region found along edge";
+        *outTop = -1;
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+static uint32_t get_color(
+    png_bytepp rows, int left, int top, int right, int bottom)
+{
+    png_bytep color = rows[top] + left*4;
+
+    if (left > right || top > bottom) {
+        return Res_png_9patch::TRANSPARENT_COLOR;
+    }
+
+    while (top <= bottom) {
+        for (int i = left; i <= right; i++) {
+            png_bytep p = rows[top]+i*4;
+            if (color[3] == 0) {
+                if (p[3] != 0) {
+                    return Res_png_9patch::NO_COLOR;
+                }
+            } else if (p[0] != color[0] || p[1] != color[1]
+                       || p[2] != color[2] || p[3] != color[3]) {
+                return Res_png_9patch::NO_COLOR;
+            }
+        }
+        top++;
+    }
+
+    if (color[3] == 0) {
+        return Res_png_9patch::TRANSPARENT_COLOR;
+    }
+    return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
+}
+
+static void select_patch(
+    int which, int front, int back, int size, int* start, int* end)
+{
+    switch (which) {
+    case 0:
+        *start = 0;
+        *end = front-1;
+        break;
+    case 1:
+        *start = front;
+        *end = back-1;
+        break;
+    case 2:
+        *start = back;
+        *end = size-1;
+        break;
+    }
+}
+
+static uint32_t get_color(image_info* image, int hpatch, int vpatch)
+{
+    int left, right, top, bottom;
+    select_patch(
+        hpatch, image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
+        image->width, &left, &right);
+    select_patch(
+        vpatch, image->info9Patch.yDivs[0], image->info9Patch.yDivs[1],
+        image->height, &top, &bottom);
+    //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
+    //       hpatch, vpatch, left, top, right, bottom);
+    const uint32_t c = get_color(image->rows, left, top, right, bottom);
+    NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left, top, right, bottom, c));
+    return c;
+}
+
+static status_t do_9patch(const char* imageName, image_info* image)
+{
+    image->is9Patch = true;
+
+    int W = image->width;
+    int H = image->height;
+    int i, j;
+
+    int maxSizeXDivs = (W / 2 + 1) * sizeof(int32_t);
+    int maxSizeYDivs = (H / 2 + 1) * sizeof(int32_t);
+    int32_t* xDivs = (int32_t*) malloc(maxSizeXDivs);
+    int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs);
+    uint8_t  numXDivs = 0;
+    uint8_t  numYDivs = 0;
+    int8_t numColors;
+    int numRows;
+    int numCols;
+    int top;
+    int left;
+    int right;
+    int bottom;
+    memset(xDivs, -1, maxSizeXDivs);
+    memset(yDivs, -1, maxSizeYDivs);
+    image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
+        image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+
+    png_bytep p = image->rows[0];
+    bool transparent = p[3] == 0;
+    bool hasColor = false;
+
+    const char* errorMsg = NULL;
+    int errorPixel = -1;
+    const char* errorEdge = "";
+
+    int colorIndex = 0;
+
+    // Validate size...
+    if (W < 3 || H < 3) {
+        errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
+        goto getout;
+    }
+
+    // Validate frame...
+    if (!transparent &&
+        (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
+        errorMsg = "Must have one-pixel frame that is either transparent or white";
+        goto getout;
+    }
+
+    // Find left and right of sizing areas...
+    if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
+                             &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
+        errorPixel = xDivs[0];
+        errorEdge = "top";
+        goto getout;
+    }
+
+    // Find top and bottom of sizing areas...
+    if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
+                           &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
+        errorPixel = yDivs[0];
+        errorEdge = "left";
+        goto getout;
+    }
+
+    // Find left and right of padding area...
+    if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
+                             &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
+        errorPixel = image->info9Patch.paddingLeft;
+        errorEdge = "bottom";
+        goto getout;
+    }
+
+    // Find top and bottom of padding area...
+    if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
+                           &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
+        errorPixel = image->info9Patch.paddingTop;
+        errorEdge = "right";
+        goto getout;
+    }
+
+    // Copy patch data into image
+    image->info9Patch.numXDivs = numXDivs;
+    image->info9Patch.numYDivs = numYDivs;
+    image->info9Patch.xDivs = xDivs;
+    image->info9Patch.yDivs = yDivs;
+
+    // If padding is not yet specified, take values from size.
+    if (image->info9Patch.paddingLeft < 0) {
+        image->info9Patch.paddingLeft = xDivs[0];
+        image->info9Patch.paddingRight = W - 2 - xDivs[1];
+    } else {
+        // Adjust value to be correct!
+        image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+    }
+    if (image->info9Patch.paddingTop < 0) {
+        image->info9Patch.paddingTop = yDivs[0];
+        image->info9Patch.paddingBottom = H - 2 - yDivs[1];
+    } else {
+        // Adjust value to be correct!
+        image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
+    }
+
+    NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
+                 image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
+                 image->info9Patch.yDivs[0], image->info9Patch.yDivs[1]));
+    NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
+                 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
+                 image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
+
+    // Remove frame from image.
+    image->rows = (png_bytepp)malloc((H-2) * png_sizeof(png_bytep));
+    for (i=0; i<(H-2); i++) {
+        image->rows[i] = image->allocRows[i+1];
+        memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
+    }
+    image->width -= 2;
+    W = image->width;
+    image->height -= 2;
+    H = image->height;
+
+    // Figure out the number of rows and columns in the N-patch
+    numCols = numXDivs + 1;
+    if (xDivs[0] == 0) {  // Column 1 is strechable
+        numCols--;
+    }
+    if (xDivs[numXDivs - 1] == W) {
+        numCols--;
+    }
+    numRows = numYDivs + 1;
+    if (yDivs[0] == 0) {  // Row 1 is strechable
+        numRows--;
+    }
+    if (yDivs[numYDivs - 1] == H) {
+        numRows--;
+    }
+    numColors = numRows * numCols;
+    image->info9Patch.numColors = numColors;
+    image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
+
+    // Fill in color information for each patch.
+
+    uint32_t c;
+    top = 0;
+
+    // The first row always starts with the top being at y=0 and the bottom
+    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
+    // the first row is stretchable along the Y axis, otherwise it is fixed.
+    // The last row always ends with the bottom being bitmap.height and the top
+    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+    // the Y axis, otherwise it is fixed.
+    //
+    // The first and last columns are similarly treated with respect to the X
+    // axis.
+    //
+    // The above is to help explain some of the special casing that goes on the
+    // code below.
+
+    // The initial yDiv and whether the first row is considered stretchable or
+    // not depends on whether yDiv[0] was zero or not.
+    for (j = (yDivs[0] == 0 ? 1 : 0);
+          j <= numYDivs && top < H;
+          j++) {
+        if (j == numYDivs) {
+            bottom = H;
+        } else {
+            bottom = yDivs[j];
+        }
+        left = 0;
+        // The initial xDiv and whether the first column is considered
+        // stretchable or not depends on whether xDiv[0] was zero or not.
+        for (i = xDivs[0] == 0 ? 1 : 0;
+              i <= numXDivs && left < W;
+              i++) {
+            if (i == numXDivs) {
+                right = W;
+            } else {
+                right = xDivs[i];
+            }
+            c = get_color(image->rows, left, top, right - 1, bottom - 1);
+            image->info9Patch.colors[colorIndex++] = c;
+            NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
+            left = right;
+        }
+        top = bottom;
+    }
+
+    assert(colorIndex == numColors);
+
+    for (i=0; i<numColors; i++) {
+        if (hasColor) {
+            if (i == 0) printf("Colors in %s:\n ", imageName);
+            printf(" #%08x", image->info9Patch.colors[i]);
+            if (i == numColors - 1) printf("\n");
+        }
+    }
+
+    image->is9Patch = true;
+    image->info9Patch.deviceToFile();
+
+getout:
+    if (errorMsg) {
+        fprintf(stderr,
+            "ERROR: 9-patch image %s malformed.\n"
+            "       %s.\n", imageName, errorMsg);
+        if (errorPixel >= 0) {
+            fprintf(stderr,
+            "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
+        } else {
+            fprintf(stderr,
+            "       Found along %s edge.\n", errorEdge);
+        }
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
+{
+    if (sizeof(void*) != sizeof(int32_t)) {
+        // can't deserialize on a non-32 bit system
+        return;
+    }
+    size_t patchSize = inPatch->serializedSize();
+    void * newData = malloc(patchSize);
+    memcpy(newData, data, patchSize);
+    Res_png_9patch* outPatch = inPatch->deserialize(newData);
+    // deserialization is done in place, so outPatch == newData
+    assert(outPatch == newData);
+    assert(outPatch->numXDivs == inPatch->numXDivs);
+    assert(outPatch->numYDivs == inPatch->numYDivs);
+    assert(outPatch->paddingLeft == inPatch->paddingLeft);
+    assert(outPatch->paddingRight == inPatch->paddingRight);
+    assert(outPatch->paddingTop == inPatch->paddingTop);
+    assert(outPatch->paddingBottom == inPatch->paddingBottom);
+    for (int i = 0; i < outPatch->numXDivs; i++) {
+        assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
+    }
+    for (int i = 0; i < outPatch->numYDivs; i++) {
+        assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
+    }
+    for (int i = 0; i < outPatch->numColors; i++) {
+        assert(outPatch->colors[i] == inPatch->colors[i]);
+    }
+    free(newData);
+}
+
+static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
+    if (!(patch1.numXDivs == patch2.numXDivs &&
+          patch1.numYDivs == patch2.numYDivs &&
+          patch1.numColors == patch2.numColors &&
+          patch1.paddingLeft == patch2.paddingLeft &&
+          patch1.paddingRight == patch2.paddingRight &&
+          patch1.paddingTop == patch2.paddingTop &&
+          patch1.paddingBottom == patch2.paddingBottom)) {
+            return false;
+    }
+    for (int i = 0; i < patch1.numColors; i++) {
+        if (patch1.colors[i] != patch2.colors[i]) {
+            return false;
+        }
+    }
+    for (int i = 0; i < patch1.numXDivs; i++) {
+        if (patch1.xDivs[i] != patch2.xDivs[i]) {
+            return false;
+        }
+    }
+    for (int i = 0; i < patch1.numYDivs; i++) {
+        if (patch1.yDivs[i] != patch2.yDivs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static void dump_image(int w, int h, png_bytepp rows, int color_type)
+{
+    int i, j, rr, gg, bb, aa;
+
+    int bpp;
+    if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
+        bpp = 1;
+    } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        bpp = 2;
+    } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+	    // We use a padding byte even when there is no alpha
+        bpp = 4;
+    } else {
+        printf("Unknown color type %d.\n", color_type);
+    }
+
+    for (j = 0; j < h; j++) {
+        png_bytep row = rows[j];
+        for (i = 0; i < w; i++) {
+            rr = row[0];
+            gg = row[1];
+            bb = row[2];
+            aa = row[3];
+            row += bpp;
+
+            if (i == 0) {
+                printf("Row %d:", j);
+            }
+            switch (bpp) {
+            case 1:
+                printf(" (%d)", rr);
+                break;
+            case 2:
+                printf(" (%d %d", rr, gg);
+                break;
+            case 3:
+                printf(" (%d %d %d)", rr, gg, bb);
+                break;
+            case 4:
+                printf(" (%d %d %d %d)", rr, gg, bb, aa);
+                break;
+            }
+            if (i == (w - 1)) {
+                NOISY(printf("\n"));
+            }
+        }
+    }
+}
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define ABS(a)   ((a)<0?-(a):(a))
+
+static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
+                          png_colorp rgbPalette, png_bytep alphaPalette,
+                          int *paletteEntries, bool *hasTransparency, int *colorType,
+                          png_bytepp outRows)
+{
+    int w = imageInfo.width;
+    int h = imageInfo.height;
+    int i, j, rr, gg, bb, aa, idx;
+    uint32_t colors[256], col;
+    int num_colors = 0;
+    int maxGrayDeviation = 0;
+
+    bool isOpaque = true;
+    bool isPalette = true;
+    bool isGrayscale = true;
+
+    // Scan the entire image and determine if:
+    // 1. Every pixel has R == G == B (grayscale)
+    // 2. Every pixel has A == 255 (opaque)
+    // 3. There are no more than 256 distinct RGBA colors
+
+    // NOISY(printf("Initial image data:\n"));
+    // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
+
+    for (j = 0; j < h; j++) {
+        png_bytep row = imageInfo.rows[j];
+        png_bytep out = outRows[j];
+        for (i = 0; i < w; i++) {
+            rr = *row++;
+            gg = *row++;
+            bb = *row++;
+            aa = *row++;
+
+            int odev = maxGrayDeviation;
+            maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
+            maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
+            maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
+            if (maxGrayDeviation > odev) {
+                NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
+                             maxGrayDeviation, i, j, rr, gg, bb, aa));
+            }
+
+            // Check if image is really grayscale
+            if (isGrayscale) {
+                if (rr != gg || rr != bb) {
+                     NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
+                                  i, j, rr, gg, bb, aa));
+                    isGrayscale = false;
+                }
+            }
+
+            // Check if image is really opaque
+            if (isOpaque) {
+                if (aa != 0xff) {
+                    NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
+                                 i, j, rr, gg, bb, aa));
+                    isOpaque = false;
+                }
+            }
+
+            // Check if image is really <= 256 colors
+            if (isPalette) {
+                col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
+                bool match = false;
+                for (idx = 0; idx < num_colors; idx++) {
+                    if (colors[idx] == col) {
+                        match = true;
+                        break;
+                    }
+                }
+
+                // Write the palette index for the pixel to outRows optimistically
+                // We might overwrite it later if we decide to encode as gray or
+                // gray + alpha
+                *out++ = idx;
+                if (!match) {
+                    if (num_colors == 256) {
+                        NOISY(printf("Found 257th color at %d, %d\n", i, j));
+                        isPalette = false;
+                    } else {
+                        colors[num_colors++] = col;
+                    }
+                }
+            }
+        }
+    }
+
+    *paletteEntries = 0;
+    *hasTransparency = !isOpaque;
+    int bpp = isOpaque ? 3 : 4;
+    int paletteSize = w * h + bpp * num_colors;
+
+    NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"));
+    NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false"));
+    NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false"));
+    NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
+                 paletteSize, 2 * w * h, bpp * w * h));
+    NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance));
+
+    // Choose the best color type for the image.
+    // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
+    // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
+    //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
+    // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
+    //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
+    if (isGrayscale) {
+        if (isOpaque) {
+            *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
+        } else {
+            // Use a simple heuristic to determine whether using a palette will
+            // save space versus using gray + alpha for each pixel.
+            // This doesn't take into account chunk overhead, filtering, LZ
+            // compression, etc.
+            if (isPalette && (paletteSize < 2 * w * h)) {
+                *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
+            } else {
+                *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
+            }
+        }
+    } else if (isPalette && (paletteSize < bpp * w * h)) {
+        *colorType = PNG_COLOR_TYPE_PALETTE;
+    } else {
+        if (maxGrayDeviation <= grayscaleTolerance) {
+            printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
+            *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+        } else {
+            *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+        }
+    }
+
+    // Perform postprocessing of the image or palette data based on the final
+    // color type chosen
+
+    if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+        // Create separate RGB and Alpha palettes and set the number of colors
+        *paletteEntries = num_colors;
+
+        // Create the RGB and alpha palettes
+        for (int idx = 0; idx < num_colors; idx++) {
+            col = colors[idx];
+            rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
+            rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
+            rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
+            alphaPalette[idx]     = (png_byte)  (col        & 0xff);
+        }
+    } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        // If the image is gray or gray + alpha, compact the pixels into outRows
+        for (j = 0; j < h; j++) {
+            png_bytep row = imageInfo.rows[j];
+            png_bytep out = outRows[j];
+            for (i = 0; i < w; i++) {
+                rr = *row++;
+                gg = *row++;
+                bb = *row++;
+                aa = *row++;
+                
+                if (isGrayscale) {
+                    *out++ = rr;
+                } else {
+                    *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+                }
+                if (!isOpaque) {
+                    *out++ = aa;
+                }
+           }
+        }
+    }
+}
+
+
+static void write_png(const char* imageName,
+                      png_structp write_ptr, png_infop write_info,
+                      image_info& imageInfo, int grayscaleTolerance)
+{
+    bool optimize = true;
+    png_uint_32 width, height;
+    int color_type;
+    int bit_depth, interlace_type, compression_type;
+    int i;
+
+    png_unknown_chunk unknowns[1];
+
+    png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
+    if (outRows == (png_bytepp) 0) {
+        printf("Can't allocate output buffer!\n");
+        exit(1);
+    }
+    for (i = 0; i < (int) imageInfo.height; i++) {
+        outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
+        if (outRows[i] == (png_bytep) 0) {
+            printf("Can't allocate output buffer!\n");
+            exit(1);
+        }
+    }
+
+    png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
+
+    NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName,
+          (int) imageInfo.width, (int) imageInfo.height));
+
+    png_color rgbPalette[256];
+    png_byte alphaPalette[256];
+    bool hasTransparency;
+    int paletteEntries;
+
+    analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
+                  &paletteEntries, &hasTransparency, &color_type, outRows);
+
+    // If the image is a 9-patch, we need to preserve it as a ARGB file to make
+    // sure the pixels will not be pre-dithered/clamped until we decide they are
+    if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
+            color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
+        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+    }
+
+    switch (color_type) {
+    case PNG_COLOR_TYPE_PALETTE:
+        NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
+                     imageName, paletteEntries,
+                     hasTransparency ? " (with alpha)" : ""));
+        break;
+    case PNG_COLOR_TYPE_GRAY:
+        NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
+        break;
+    case PNG_COLOR_TYPE_GRAY_ALPHA:
+        NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
+        break;
+    case PNG_COLOR_TYPE_RGB:
+        NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
+        break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+        NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
+        break;
+    }
+
+    png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
+                 8, color_type, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
+        if (hasTransparency) {
+            png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
+        }
+       png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
+    } else {
+       png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
+    }
+
+    if (imageInfo.is9Patch) {
+        NOISY(printf("Adding 9-patch info...\n"));
+        strcpy((char*)unknowns[0].name, "npTc");
+        unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
+        unknowns[0].size = imageInfo.info9Patch.serializedSize();
+        // TODO: remove the check below when everything works
+        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
+        png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
+                                    (png_byte*)"npTc", 1);
+        png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
+        // XXX I can't get this to work without forcibly changing
+        // the location to what I want...  which apparently is supposed
+        // to be a private API, but everything else I have tried results
+        // in the location being set to what I -last- wrote so I never
+        // get written. :p
+        png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
+    }
+
+    png_write_info(write_ptr, write_info);
+
+    png_bytepp rows;
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+        png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
+        rows = imageInfo.rows;
+    } else {
+        rows = outRows;
+    }
+    png_write_image(write_ptr, rows);
+
+//     NOISY(printf("Final image data:\n"));
+//     dump_image(imageInfo.width, imageInfo.height, rows, color_type);
+
+    png_write_end(write_ptr, write_info);
+
+    for (i = 0; i < (int) imageInfo.height; i++) {
+        free(outRows[i]);
+    }
+    free(outRows);
+
+    png_get_IHDR(write_ptr, write_info, &width, &height,
+       &bit_depth, &color_type, &interlace_type,
+       &compression_type, NULL);
+
+    NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
+                 (int)width, (int)height, bit_depth, color_type, interlace_type,
+                 compression_type));
+}
+
+status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
+                         const sp<AaptFile>& file, String8* outNewLeafName)
+{
+    String8 ext(file->getPath().getPathExtension());
+
+    // We currently only process PNG images.
+    if (strcmp(ext.string(), ".png") != 0) {
+        return NO_ERROR;
+    }
+
+    // Example of renaming a file:
+    //*outNewLeafName = file->getPath().getBasePath().getFileName();
+    //outNewLeafName->append(".nupng");
+
+    String8 printableName(file->getPrintableSource());
+
+    png_structp read_ptr = NULL;
+    png_infop read_info = NULL;
+    FILE* fp;
+
+    image_info imageInfo;
+
+    png_structp write_ptr = NULL;
+    png_infop write_info = NULL;
+
+    status_t error = UNKNOWN_ERROR;
+
+    const size_t nameLen = file->getPath().length();
+
+    fp = fopen(file->getSourceFile().string(), "rb");
+    if (fp == NULL) {
+        fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
+        goto bail;
+    }
+
+    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
+                                        (png_error_ptr)NULL);
+    if (!read_ptr) {
+        goto bail;
+    }
+
+    read_info = png_create_info_struct(read_ptr);
+    if (!read_info) {
+        goto bail;
+    }
+
+    if (setjmp(png_jmpbuf(read_ptr))) {
+        goto bail;
+    }
+
+    png_init_io(read_ptr, fp);
+
+    read_png(printableName.string(), read_ptr, read_info, &imageInfo);
+
+    if (nameLen > 6) {
+        const char* name = file->getPath().string();
+        if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
+            if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
+                goto bail;
+            }
+        }
+    }
+
+    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
+                                        (png_error_ptr)NULL);
+    if (!write_ptr)
+    {
+        goto bail;
+    }
+
+    write_info = png_create_info_struct(write_ptr);
+    if (!write_info)
+    {
+        goto bail;
+    }
+
+    png_set_write_fn(write_ptr, (void*)file.get(),
+                     png_write_aapt_file, png_flush_aapt_file);
+
+    if (setjmp(png_jmpbuf(write_ptr)))
+    {
+        goto bail;
+    }
+
+    write_png(printableName.string(), write_ptr, write_info, imageInfo,
+              bundle->getGrayscaleTolerance());
+
+    error = NO_ERROR;
+
+    if (bundle->getVerbose()) {
+        fseek(fp, 0, SEEK_END);
+        size_t oldSize = (size_t)ftell(fp);
+        size_t newSize = file->getSize();
+        float factor = ((float)newSize)/oldSize;
+        int percent = (int)(factor*100);
+        printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
+    }
+
+bail:
+    if (read_ptr) {
+        png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
+    }
+    if (fp) {
+        fclose(fp);
+    }
+    if (write_ptr) {
+        png_destroy_write_struct(&write_ptr, &write_info);
+    }
+
+    if (error != NO_ERROR) {
+        fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
+                file->getPrintableSource().string());
+    }
+    return error;
+}
+
+
+
+status_t postProcessImage(const sp<AaptAssets>& assets,
+                          ResourceTable* table, const sp<AaptFile>& file)
+{
+    String8 ext(file->getPath().getPathExtension());
+
+    // At this point, now that we have all the resource data, all we need to
+    // do is compile XML files.
+    if (strcmp(ext.string(), ".xml") == 0) {
+        return compileXmlFile(assets, file, table);
+    }
+
+    return NO_ERROR;
+}
diff --git a/tools/aapt/Images.h b/tools/aapt/Images.h
new file mode 100644
index 0000000..168e22f
--- /dev/null
+++ b/tools/aapt/Images.h
@@ -0,0 +1,18 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef IMAGES_H
+#define IMAGES_H
+
+#include "ResourceTable.h"
+
+status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
+                         const sp<AaptFile>& file, String8* outNewLeafName);
+
+status_t postProcessImage(const sp<AaptAssets>& assets,
+						  ResourceTable* table, const sp<AaptFile>& file);
+
+#endif
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
new file mode 100644
index 0000000..71b1a3c
--- /dev/null
+++ b/tools/aapt/Main.cpp
@@ -0,0 +1,369 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Android Asset Packaging Tool main entry point.
+//
+#include "Main.h"
+#include "Bundle.h"
+
+#include <utils.h>
+#include <utils/ZipFile.h>
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <assert.h>
+
+using namespace android;
+
+static const char* gProgName = "aapt";
+
+/*
+ * When running under Cygwin on Windows, this will convert slash-based
+ * paths into back-slash-based ones. Otherwise the ApptAssets file comparisons
+ * fail later as they use back-slash separators under Windows.
+ *
+ * This operates in-place on the path string.
+ */
+void convertPath(char *path) {
+  if (path != NULL && OS_PATH_SEPARATOR != '/') {
+    for (; *path; path++) {
+      if (*path == '/') {
+        *path = OS_PATH_SEPARATOR;
+      }
+    }
+  }
+}
+
+/*
+ * Print usage info.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Android Asset Packaging Tool\n\n");
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr,
+        " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n"
+        "   List contents of Zip-compatible archive.\n\n", gProgName);
+    fprintf(stderr,
+        " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n"
+        "   badging          Print the label and icon for the app declared in APK.\n"
+        "   permissions      Print the permissions from the APK.\n"
+        "   resources        Print the resource table from the APK.\n"
+        "   configurations   Print the configurations in the APK.\n"
+        "   xmltree          Print the compiled xmls in the given assets.\n"
+        "   xmlstrings       Print the strings of the given compiled xml assets.\n\n", gProgName);
+    fprintf(stderr,
+        " %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n"
+        "        [-0 extension [-0 extension ...]] \\\n"
+        "        [-g tolerance] \\\n"
+        "        [-j jarfile] \\\n"
+        "        [-I base-package [-I base-package ...]] \\\n"
+        "        [-A asset-source-dir] [-P public-definitions-file] \\\n"
+        "        [-S resource-sources [-S resource-sources ...]] "
+        "        [-F apk-file] [-J R-file-dir] \\\n"
+        "        [raw-files-dir [raw-files-dir] ...]\n"
+        "\n"
+        "   Package the android resources.  It will read assets and resources that are\n"
+        "   supplied with the -M -A -S or raw-files-dir arguments.  The -J -P -F and -R\n"
+        "   options control which files are output.\n\n"
+        , gProgName);
+    fprintf(stderr,
+        " %s r[emove] [-v] file.{zip,jar,apk} file1 [file2 ...]\n"
+        "   Delete specified files from Zip-compatible archive.\n\n",
+        gProgName);
+    fprintf(stderr,
+        " %s a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]\n"
+        "   Add specified files to Zip-compatible archive.\n\n", gProgName);
+    fprintf(stderr,
+        " %s v[ersion]\n"
+        "   Print program version.\n\n", gProgName);
+    fprintf(stderr,
+        " Modifiers:\n"
+        "   -a  print Android-specific data (resources, manifest) when listing\n"
+        "   -c  specify which configurations to include.  The default is all\n"
+        "       configurations.  The value of the parameter should be a comma\n"
+        "       separated list of configuration values.  Locales should be specified\n"
+        "       as either a language or language-region pair.  Some examples:\n"
+        "            en\n"
+        "            port,en\n"
+        "            port,land,en_US\n"
+        "       If you put the special locale, zz_ZZ on the list, it will perform\n"
+        "       pseudolocalization on the default locale, modifying all of the\n"
+        "       strings so you can look for strings that missed the\n"
+        "       internationalization process.  For example:\n"
+        "            port,land,zz_ZZ\n"
+        "   -d  one or more device assets to include, separated by commas\n"
+        "   -f  force overwrite of existing files\n"
+        "   -g  specify a pixel tolerance to force images to grayscale, default 0\n"
+        "   -j  specify a jar or zip file containing classes to include\n"
+        "   -m  make package directories under location specified by -J\n"
+#if 0
+        "   -p  pseudolocalize the default configuration\n"
+#endif
+        "   -u  update existing packages (add new, replace older, remove deleted files)\n"
+        "   -v  verbose output\n"
+        "   -x  create extending (non-application) resource IDs\n"
+        "   -z  require localization of resource attributes marked with\n"
+        "       localization=\"suggested\"\n"
+        "   -A  additional directory in which to find raw asset files\n"
+        "   -F  specify the apk file to output\n"
+        "   -I  add an existing package to base include set\n"
+        "   -J  specify where to output R.java resource constant definitions\n"
+        "   -M  specify full path to AndroidManifest.xml to include in zip\n"
+        "   -P  specify where to output public resource definitions\n"
+        "   -S  directory in which to find resources.  Multiple directories will be scanned"
+        "       and the first match found (left to right) will take precedence."
+        "   -0  specifies an additional extension for which such files will not\n"
+        "       be stored compressed in the .apk.  An empty string means to not\n"
+        "       compress any files at all.\n");
+}
+
+/*
+ * Dispatch the command.
+ */
+int handleCommand(Bundle* bundle)
+{
+    //printf("--- command %d (verbose=%d force=%d):\n",
+    //    bundle->getCommand(), bundle->getVerbose(), bundle->getForce());
+    //for (int i = 0; i < bundle->getFileSpecCount(); i++)
+    //    printf("  %d: '%s'\n", i, bundle->getFileSpecEntry(i));
+
+    switch (bundle->getCommand()) {
+    case kCommandVersion:   return doVersion(bundle);
+    case kCommandList:      return doList(bundle);
+    case kCommandDump:      return doDump(bundle);
+    case kCommandAdd:       return doAdd(bundle);
+    case kCommandRemove:    return doRemove(bundle);
+    case kCommandPackage:   return doPackage(bundle);
+    default:
+        fprintf(stderr, "%s: requested command not yet supported\n", gProgName);
+        return 1;
+    }
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    char *prog = argv[0];
+    Bundle bundle;
+    bool wantUsage = false;
+    int result = 1;    // pessimistically assume an error.
+    int tolerance = 0;
+
+    /* default to compression */
+    bundle.setCompressionMethod(ZipEntry::kCompressDeflated);
+
+    if (argc < 2) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    if (argv[1][0] == 'v')
+        bundle.setCommand(kCommandVersion);
+    else if (argv[1][0] == 'd')
+        bundle.setCommand(kCommandDump);
+    else if (argv[1][0] == 'l')
+        bundle.setCommand(kCommandList);
+    else if (argv[1][0] == 'a')
+        bundle.setCommand(kCommandAdd);
+    else if (argv[1][0] == 'r')
+        bundle.setCommand(kCommandRemove);
+    else if (argv[1][0] == 'p')
+        bundle.setCommand(kCommandPackage);
+    else {
+        fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]);
+        wantUsage = true;
+        goto bail;
+    }
+    argc -= 2;
+    argv += 2;
+
+    /*
+     * Pull out flags.  We support "-fv" and "-f -v".
+     */
+    while (argc && argv[0][0] == '-') {
+        /* flag(s) found */
+        const char* cp = argv[0] +1;
+
+        while (*cp != '\0') {
+            switch (*cp) {
+            case 'v':
+                bundle.setVerbose(true);
+                break;
+            case 'a':
+                bundle.setAndroidList(true);
+                break;
+            case 'c':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-c' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                bundle.addConfigurations(argv[0]);
+                break;
+            case 'f':
+                bundle.setForce(true);
+                break;
+            case 'g':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-g' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                tolerance = atoi(argv[0]);
+                bundle.setGrayscaleTolerance(tolerance);
+                printf("%s: Images with deviation <= %d will be forced to grayscale.\n", prog, tolerance);
+                break;
+            case 'm':
+                bundle.setMakePackageDirs(true);
+                break;
+#if 0
+            case 'p':
+                bundle.setPseudolocalize(true);
+                break;
+#endif
+            case 'u':
+                bundle.setUpdate(true);
+                break;
+            case 'x':
+                bundle.setExtending(true);
+                break;
+            case 'z':
+                bundle.setRequireLocalization(true);
+                break;
+            case 'j':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-j' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.addJarFile(argv[0]);
+                break;
+            case 'A':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-A' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setAssetSourceDir(argv[0]);
+                break;
+            case 'I':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-I' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.addPackageInclude(argv[0]);
+                break;
+            case 'F':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-F' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setOutputAPKFile(argv[0]);
+                break;
+            case 'J':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-J' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setRClassDir(argv[0]);
+                break;
+            case 'M':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-M' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setAndroidManifestFile(argv[0]);
+                break;
+            case 'P':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-P' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setPublicOutputFile(argv[0]);
+                break;
+            case 'S':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-S' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.addResourceSourceDir(argv[0]);
+                break;
+            case '0':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-e' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                if (argv[0][0] != 0) {
+                    bundle.addNoCompressExtension(argv[0]);
+                } else {
+                    bundle.setCompressionMethod(ZipEntry::kCompressStored);
+                }
+                break;
+            default:
+                fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);
+                wantUsage = true;
+                goto bail;
+            }
+
+            cp++;
+        }
+        argc--;
+        argv++;
+    }
+
+    /*
+     * We're past the flags.  The rest all goes straight in.
+     */
+    bundle.setFileSpec(argv, argc);
+
+    result = handleCommand(&bundle);
+
+bail:
+    if (wantUsage) {
+        usage();
+        result = 2;
+    }
+
+    //printf("--> returning %d\n", result);
+    return result;
+}
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
new file mode 100644
index 0000000..65c0a8a
--- /dev/null
+++ b/tools/aapt/Main.h
@@ -0,0 +1,41 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Some global defines that don't really merit their own header.
+//
+#ifndef __MAIN_H
+#define __MAIN_H
+
+#include <utils.h>
+#include "Bundle.h"
+#include "AaptAssets.h"
+#include <utils/ZipFile.h>
+
+extern int doVersion(Bundle* bundle);
+extern int doList(Bundle* bundle);
+extern int doDump(Bundle* bundle);
+extern int doAdd(Bundle* bundle);
+extern int doRemove(Bundle* bundle);
+extern int doPackage(Bundle* bundle);
+
+extern int calcPercent(long uncompressedLen, long compressedLen);
+
+extern android::status_t writeAPK(Bundle* bundle,
+    const sp<AaptAssets>& assets,
+    const android::String8& outputFile);
+
+extern android::status_t buildResources(Bundle* bundle,
+    const sp<AaptAssets>& assets);
+
+extern android::status_t writeResourceSymbols(Bundle* bundle,
+    const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
+
+extern bool isValidResourceType(const String8& type);
+
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
+
+extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
+
+int dumpResources(Bundle* bundle);
+
+#endif // __MAIN_H
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
new file mode 100644
index 0000000..eb7d6f5
--- /dev/null
+++ b/tools/aapt/Package.cpp
@@ -0,0 +1,464 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Package assets into Zip files.
+//
+#include "Main.h"
+#include "AaptAssets.h"
+#include "ResourceTable.h"
+
+#include <utils.h>
+#include <utils/ZipFile.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+
+using namespace android;
+
+static const char* kExcludeExtension = ".EXCLUDE";
+
+/* these formats are already compressed, or don't compress well */
+static const char* kNoCompressExt[] = {
+    ".jpg", ".jpeg", ".png", ".gif",
+    ".wav", ".mp2", ".mp3", ".ogg", ".aac",
+    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
+    ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
+    ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
+    ".amr", ".awb", ".wma", ".wmv"
+};
+
+/* fwd decls, so I can write this downward */
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip,
+                        const sp<AaptDir>& dir, const AaptGroupEntry& ge);
+bool processFile(Bundle* bundle, ZipFile* zip,
+                        const sp<AaptGroup>& group, const sp<AaptFile>& file);
+bool okayToCompress(Bundle* bundle, const String8& pathName);
+ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
+
+/*
+ * The directory hierarchy looks like this:
+ * "outputDir" and "assetRoot" are existing directories.
+ *
+ * On success, "bundle->numPackages" will be the number of Zip packages
+ * we created.
+ */
+status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
+                       const String8& outputFile)
+{
+    status_t result = NO_ERROR;
+    ZipFile* zip = NULL;
+    int count;
+
+    //bundle->setPackageCount(0);
+
+    /*
+     * Prep the Zip archive.
+     *
+     * If the file already exists, fail unless "update" or "force" is set.
+     * If "update" is set, update the contents of the existing archive.
+     * Else, if "force" is set, remove the existing archive.
+     */
+    FileType fileType = getFileType(outputFile.string());
+    if (fileType == kFileTypeNonexistent) {
+        // okay, create it below
+    } else if (fileType == kFileTypeRegular) {
+        if (bundle->getUpdate()) {
+            // okay, open it below
+        } else if (bundle->getForce()) {
+            if (unlink(outputFile.string()) != 0) {
+                fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.string(),
+                        strerror(errno));
+                goto bail;
+            }
+        } else {
+            fprintf(stderr, "ERROR: '%s' exists (use '-f' to force overwrite)\n",
+                    outputFile.string());
+            goto bail;
+        }
+    } else {
+        fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.string());
+        goto bail;
+    }
+
+    if (bundle->getVerbose()) {
+        printf("%s '%s'\n", (fileType == kFileTypeNonexistent) ? "Creating" : "Opening",
+                outputFile.string());
+    }
+
+    status_t status;
+    zip = new ZipFile;
+    status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate);
+    if (status != NO_ERROR) {
+        fprintf(stderr, "ERROR: unable to open '%s' as Zip file for writing\n",
+                outputFile.string());
+        goto bail;
+    }
+
+    if (bundle->getVerbose()) {
+        printf("Writing all files...\n");
+    }
+
+    count = processAssets(bundle, zip, assets);
+    if (count < 0) {
+        fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
+                outputFile.string());
+        result = count;
+        goto bail;
+    }
+
+    if (bundle->getVerbose()) {
+        printf("Generated %d file%s\n", count, (count==1) ? "" : "s");
+    }
+    
+    count = processJarFiles(bundle, zip);
+    if (count < 0) {
+        fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n",
+                outputFile.string());
+        result = count;
+        goto bail;
+    }
+    
+    if (bundle->getVerbose())
+        printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s");
+    
+    result = NO_ERROR;
+
+    /*
+     * Check for cruft.  We set the "marked" flag on all entries we created
+     * or decided not to update.  If the entry isn't already slated for
+     * deletion, remove it now.
+     */
+    {
+        if (bundle->getVerbose())
+            printf("Checking for deleted files\n");
+        int i, removed = 0;
+        for (i = 0; i < zip->getNumEntries(); i++) {
+            ZipEntry* entry = zip->getEntryByIndex(i);
+
+            if (!entry->getMarked() && entry->getDeleted()) {
+                if (bundle->getVerbose()) {
+                    printf("      (removing crufty '%s')\n",
+                        entry->getFileName());
+                }
+                zip->remove(entry);
+                removed++;
+            }
+        }
+        if (bundle->getVerbose() && removed > 0)
+            printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s");
+    }
+
+    /* tell Zip lib to process deletions and other pending changes */
+    result = zip->flush();
+    if (result != NO_ERROR) {
+        fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n");
+        goto bail;
+    }
+
+    /* anything here? */
+    if (zip->getNumEntries() == 0) {
+        if (bundle->getVerbose()) {
+            printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().string());
+        }
+        delete zip;        // close the file so we can remove it in Win32
+        zip = NULL;
+        if (unlink(outputFile.string()) != 0) {
+            fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string());
+        }
+    }
+
+    assert(result == NO_ERROR);
+
+bail:
+    delete zip;        // must close before remove in Win32
+    if (result != NO_ERROR) {
+        if (bundle->getVerbose()) {
+            printf("Removing %s due to earlier failures\n", outputFile.string());
+        }
+        if (unlink(outputFile.string()) != 0) {
+            fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string());
+        }
+    }
+
+    if (result == NO_ERROR && bundle->getVerbose())
+        printf("Done!\n");
+    return result;
+}
+
+ssize_t processAssets(Bundle* bundle, ZipFile* zip,
+                      const sp<AaptAssets>& assets)
+{
+    ResourceFilter filter;
+    status_t status = filter.parse(bundle->getConfigurations());
+    if (status != NO_ERROR) {
+        return -1;
+    }
+
+    ssize_t count = 0;
+
+    const size_t N = assets->getGroupEntries().size();
+    for (size_t i=0; i<N; i++) {
+        const AaptGroupEntry& ge = assets->getGroupEntries()[i];
+        if (!filter.match(ge.toParams())) {
+            continue;
+        }
+        ssize_t res = processAssets(bundle, zip, assets, ge);
+        if (res < 0) {
+            return res;
+        }
+        count += res;
+    }
+
+    return count;
+}
+
+ssize_t processAssets(Bundle* bundle, ZipFile* zip,
+                      const sp<AaptDir>& dir, const AaptGroupEntry& ge)
+{
+    ssize_t count = 0;
+
+    const size_t ND = dir->getDirs().size();
+    size_t i;
+    for (i=0; i<ND; i++) {
+        ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge);
+        if (res < 0) {
+            return res;
+        }
+        count += res;
+    }
+
+    const size_t NF = dir->getFiles().size();
+    for (i=0; i<NF; i++) {
+        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
+        ssize_t fi = gp->getFiles().indexOfKey(ge);
+        if (fi >= 0) {
+            sp<AaptFile> fl = gp->getFiles().valueAt(fi);
+            if (!processFile(bundle, zip, gp, fl)) {
+                return UNKNOWN_ERROR;
+            }
+            count++;
+        }
+    }
+
+    return count;
+}
+
+/*
+ * Process a regular file, adding it to the archive if appropriate.
+ *
+ * If we're in "update" mode, and the file already exists in the archive,
+ * delete the existing entry before adding the new one.
+ */
+bool processFile(Bundle* bundle, ZipFile* zip,
+                 const sp<AaptGroup>& group, const sp<AaptFile>& file)
+{
+    const bool hasData = file->hasData();
+
+    String8 storageName(group->getPath());
+    storageName.convertToResPath();
+    ZipEntry* entry;
+    bool fromGzip = false;
+    status_t result;
+
+    /*
+     * See if the filename ends in ".EXCLUDE".  We can't use
+     * String8::getPathExtension() because the length of what it considers
+     * to be an extension is capped.
+     *
+     * The Asset Manager doesn't check for ".EXCLUDE" in Zip archives,
+     * so there's no value in adding them (and it makes life easier on
+     * the AssetManager lib if we don't).
+     *
+     * NOTE: this restriction has been removed.  If you're in this code, you
+     * should clean this up, but I'm in here getting rid of Path Name, and I
+     * don't want to make other potentially breaking changes --joeo
+     */
+    int fileNameLen = storageName.length();
+    int excludeExtensionLen = strlen(kExcludeExtension);
+    if (fileNameLen > excludeExtensionLen
+            && (0 == strcmp(storageName.string() + (fileNameLen - excludeExtensionLen),
+                            kExcludeExtension))) {
+        fprintf(stderr, "WARNING: '%s' not added to Zip\n", storageName.string());
+        return true;
+    }
+
+    if (strcasecmp(storageName.getPathExtension().string(), ".gz") == 0) {
+        fromGzip = true;
+        storageName = storageName.getBasePath();
+    }
+
+    if (bundle->getUpdate()) {
+        entry = zip->getEntryByName(storageName.string());
+        if (entry != NULL) {
+            /* file already exists in archive; there can be only one */
+            if (entry->getMarked()) {
+                fprintf(stderr,
+                        "ERROR: '%s' exists twice (check for with & w/o '.gz'?)\n",
+                        file->getPrintableSource().string());
+                return false;
+            }
+            if (!hasData) {
+                const String8& srcName = file->getSourceFile();
+                time_t fileModWhen;
+                fileModWhen = getFileModDate(srcName.string());
+                if (fileModWhen == (time_t) -1) { // file existence tested earlier,
+                    return false;                 //  not expecting an error here
+                }
+    
+                if (fileModWhen > entry->getModWhen()) {
+                    // mark as deleted so add() will succeed
+                    if (bundle->getVerbose()) {
+                        printf("      (removing old '%s')\n", storageName.string());
+                    }
+    
+                    zip->remove(entry);
+                } else {
+                    // version in archive is newer
+                    if (bundle->getVerbose()) {
+                        printf("      (not updating '%s')\n", storageName.string());
+                    }
+                    entry->setMarked(true);
+                    return true;
+                }
+            } else {
+                // Generated files are always replaced.
+                zip->remove(entry);
+            }
+        }
+    }
+
+    //android_setMinPriority(NULL, ANDROID_LOG_VERBOSE);
+
+    if (fromGzip) {
+        result = zip->addGzip(file->getSourceFile().string(), storageName.string(), &entry);
+    } else if (!hasData) {
+        /* don't compress certain files, e.g. PNGs */
+        int compressionMethod = bundle->getCompressionMethod();
+        if (!okayToCompress(bundle, storageName)) {
+            compressionMethod = ZipEntry::kCompressStored;
+        }
+        result = zip->add(file->getSourceFile().string(), storageName.string(), compressionMethod,
+                            &entry);
+    } else {
+        result = zip->add(file->getData(), file->getSize(), storageName.string(),
+                           file->getCompressionMethod(), &entry);
+    }
+    if (result == NO_ERROR) {
+        if (bundle->getVerbose()) {
+            printf("      '%s'%s", storageName.string(), fromGzip ? " (from .gz)" : "");
+            if (entry->getCompressionMethod() == ZipEntry::kCompressStored) {
+                printf(" (not compressed)\n");
+            } else {
+                printf(" (compressed %d%%)\n", calcPercent(entry->getUncompressedLen(),
+                            entry->getCompressedLen()));
+            }
+        }
+        entry->setMarked(true);
+    } else {
+        if (result == ALREADY_EXISTS) {
+            fprintf(stderr, "      Unable to add '%s': file already in archive (try '-u'?)\n",
+                    file->getPrintableSource().string());
+        } else {
+            fprintf(stderr, "      Unable to add '%s': Zip add failed\n", 
+                    file->getPrintableSource().string());
+        }
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Determine whether or not we want to try to compress this file based
+ * on the file extension.
+ */
+bool okayToCompress(Bundle* bundle, const String8& pathName)
+{
+    String8 ext = pathName.getPathExtension();
+    int i;
+
+    if (ext.length() == 0)
+        return true;
+
+    for (i = 0; i < NELEM(kNoCompressExt); i++) {
+        if (strcasecmp(ext.string(), kNoCompressExt[i]) == 0)
+            return false;
+    }
+
+    const android::Vector<const char*>& others(bundle->getNoCompressExtensions());
+    for (i = 0; i < (int)others.size(); i++) {
+        const char* str = others[i];
+        int pos = pathName.length() - strlen(str);
+        if (pos < 0) {
+            continue;
+        }
+        const char* path = pathName.string();
+        if (strcasecmp(path + pos, str) == 0) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool endsWith(const char* haystack, const char* needle)
+{
+    size_t a = strlen(haystack);
+    size_t b = strlen(needle);
+    if (a < b) return false;
+    return strcasecmp(haystack+(a-b), needle) == 0;
+}
+
+ssize_t processJarFile(ZipFile* jar, ZipFile* out)
+{
+    status_t err;
+    size_t N = jar->getNumEntries();
+    size_t count = 0;
+    for (size_t i=0; i<N; i++) {
+        ZipEntry* entry = jar->getEntryByIndex(i);
+        const char* storageName = entry->getFileName();
+        if (endsWith(storageName, ".class")) {
+            int compressionMethod = entry->getCompressionMethod();
+            size_t size = entry->getUncompressedLen();
+            const void* data = jar->uncompress(entry);
+            if (data == NULL) {
+                fprintf(stderr, "ERROR: unable to uncompress entry '%s'\n",
+                    storageName);
+                return -1;
+            }
+            out->add(data, size, storageName, compressionMethod, NULL);
+            free((void*)data);
+        }
+        count++;
+    }
+    return count;
+}
+
+ssize_t processJarFiles(Bundle* bundle, ZipFile* zip)
+{
+    ssize_t err;
+    ssize_t count = 0;
+    const android::Vector<const char*>& jars = bundle->getJarFiles();
+
+    size_t N = jars.size();
+    for (size_t i=0; i<N; i++) {
+        ZipFile jar;
+        err = jar.open(jars[i], ZipFile::kOpenReadOnly);
+        if (err != 0) {
+            fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %zd\n",
+                jars[i], err);
+            return err;
+        }
+        err += processJarFile(&jar, zip);
+        if (err < 0) {
+            fprintf(stderr, "ERROR: unable to process '%s'\n", jars[i]);
+            return err;
+        }
+        count += err;
+    }
+
+    return count;
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
new file mode 100644
index 0000000..b2bd9ff
--- /dev/null
+++ b/tools/aapt/Resource.cpp
@@ -0,0 +1,1524 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+#include "Main.h"
+#include "AaptAssets.h"
+#include "StringPool.h"
+#include "XMLNode.h"
+#include "ResourceTable.h"
+#include "Images.h"
+
+#define NOISY(x) // x
+
+// ==========================================================================
+// ==========================================================================
+// ==========================================================================
+
+class PackageInfo
+{
+public:
+    PackageInfo()
+    {
+    }
+    ~PackageInfo()
+    {
+    }
+
+    status_t parsePackage(const sp<AaptGroup>& grp);
+};
+
+// ==========================================================================
+// ==========================================================================
+// ==========================================================================
+
+static String8 parseResourceName(const String8& leaf)
+{
+    const char* firstDot = strchr(leaf.string(), '.');
+    const char* str = leaf.string();
+
+    if (firstDot) {
+        return String8(str, firstDot-str);
+    } else {
+        return String8(str);
+    }
+}
+
+class ResourceTypeSet : public RefBase,
+                        public KeyedVector<String8,sp<AaptGroup> >
+{
+public:
+    ResourceTypeSet();
+};
+
+ResourceTypeSet::ResourceTypeSet()
+    :RefBase(),
+     KeyedVector<String8,sp<AaptGroup> >()
+{
+}
+
+class ResourceDirIterator
+{
+public:
+    ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
+        : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
+    {
+    }
+
+    inline const sp<AaptGroup>& getGroup() const { return mGroup; }
+    inline const sp<AaptFile>& getFile() const { return mFile; }
+
+    inline const String8& getBaseName() const { return mBaseName; }
+    inline const String8& getLeafName() const { return mLeafName; }
+    inline String8 getPath() const { return mPath; }
+    inline const ResTable_config& getParams() const { return mParams; }
+
+    enum {
+        EOD = 1
+    };
+
+    ssize_t next()
+    {
+        while (true) {
+            sp<AaptGroup> group;
+            sp<AaptFile> file;
+
+            // Try to get next file in this current group.
+            if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
+                group = mGroup;
+                file = group->getFiles().valueAt(mGroupPos++);
+
+            // Try to get the next group/file in this directory
+            } else if (mSetPos < mSet->size()) {
+                mGroup = group = mSet->valueAt(mSetPos++);
+                if (group->getFiles().size() < 1) {
+                    continue;
+                }
+                file = group->getFiles().valueAt(0);
+                mGroupPos = 1;
+
+            // All done!
+            } else {
+                return EOD;
+            }
+
+            mFile = file;
+
+            String8 leaf(group->getLeaf());
+            mLeafName = String8(leaf);
+            mParams = file->getGroupEntry().toParams();
+            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
+                   group->getPath().string(), mParams.mcc, mParams.mnc,
+                   mParams.language[0] ? mParams.language[0] : '-',
+                   mParams.language[1] ? mParams.language[1] : '-',
+                   mParams.country[0] ? mParams.country[0] : '-',
+                   mParams.country[1] ? mParams.country[1] : '-',
+                   mParams.orientation,
+                   mParams.density, mParams.touchscreen, mParams.keyboard,
+                   mParams.inputFlags, mParams.navigation));
+            mPath = "res";
+            mPath.appendPath(file->getGroupEntry().toDirName(mResType));
+            mPath.appendPath(leaf);
+            mBaseName = parseResourceName(leaf);
+            if (mBaseName == "") {
+                fprintf(stderr, "Error: malformed resource filename %s\n",
+                        file->getPrintableSource().string());
+                return UNKNOWN_ERROR;
+            }
+
+            NOISY(printf("file name=%s\n", mBaseName.string()));
+
+            return NO_ERROR;
+        }
+    }
+
+private:
+    String8 mResType;
+
+    const sp<ResourceTypeSet> mSet;
+    size_t mSetPos;
+
+    sp<AaptGroup> mGroup;
+    size_t mGroupPos;
+
+    sp<AaptFile> mFile;
+    String8 mBaseName;
+    String8 mLeafName;
+    String8 mPath;
+    ResTable_config mParams;
+};
+
+// ==========================================================================
+// ==========================================================================
+// ==========================================================================
+
+bool isValidResourceType(const String8& type)
+{
+    return type == "anim" || type == "drawable" || type == "layout"
+        || type == "values" || type == "xml" || type == "raw"
+        || type == "color" || type == "menu";
+}
+
+static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
+{
+    sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
+    sp<AaptFile> file;
+    if (group != NULL) {
+        file = group->getFiles().valueFor(AaptGroupEntry());
+        if (file != NULL) {
+            return file;
+        }
+    }
+
+    if (!makeIfNecessary) {
+        return NULL;
+    }
+    return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
+                            NULL, String8());
+}
+
+static status_t parsePackage(const sp<AaptAssets>& assets, const sp<AaptGroup>& grp)
+{
+    if (grp->getFiles().size() != 1) {
+        fprintf(stderr, "WARNING: Multiple AndroidManifest.xml files found, using %s\n",
+                grp->getFiles().valueAt(0)->getPrintableSource().string());
+    }
+
+    sp<AaptFile> file = grp->getFiles().valueAt(0);
+
+    ResXMLTree block;
+    status_t err = parseXMLResource(file, &block);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    //printXMLBlock(&block);
+
+    ResXMLTree::event_code_t code;
+    while ((code=block.next()) != ResXMLTree::START_TAG
+           && code != ResXMLTree::END_DOCUMENT
+           && code != ResXMLTree::BAD_DOCUMENT) {
+    }
+
+    size_t len;
+    if (code != ResXMLTree::START_TAG) {
+        fprintf(stderr, "%s:%d: No start tag found\n",
+                file->getPrintableSource().string(), block.getLineNumber());
+        return UNKNOWN_ERROR;
+    }
+    if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
+        fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
+                file->getPrintableSource().string(), block.getLineNumber(),
+                String8(block.getElementName(&len)).string());
+        return UNKNOWN_ERROR;
+    }
+
+    ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
+    if (nameIndex < 0) {
+        fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
+                file->getPrintableSource().string(), block.getLineNumber());
+        return UNKNOWN_ERROR;
+    }
+
+    assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
+
+    return NO_ERROR;
+}
+
+// ==========================================================================
+// ==========================================================================
+// ==========================================================================
+
+static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
+                                  ResourceTable* table,
+                                  const sp<ResourceTypeSet>& set,
+                                  const char* resType)
+{
+    String8 type8(resType);
+    String16 type16(resType);
+
+    bool hasErrors = false;
+
+    ResourceDirIterator it(set, String8(resType));
+    ssize_t res;
+    while ((res=it.next()) == NO_ERROR) {
+        if (bundle->getVerbose()) {
+            printf("    (new resource id %s from %s)\n",
+                   it.getBaseName().string(), it.getFile()->getPrintableSource().string());
+        }
+        String16 baseName(it.getBaseName());
+        const char16_t* str = baseName.string();
+        const char16_t* const end = str + baseName.size();
+        while (str < end) {
+            if (!((*str >= 'a' && *str <= 'z')
+                    || (*str >= '0' && *str <= '9')
+                    || *str == '_' || *str == '.')) {
+                fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
+                        it.getPath().string());
+                hasErrors = true;
+            }
+            str++;
+        }
+        String8 resPath = it.getPath();
+        resPath.convertToResPath();
+        table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
+                        type16,
+                        baseName,
+                        String16(resPath),
+                        NULL,
+                        &it.getParams());
+        assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
+    }
+
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
+                          const sp<ResourceTypeSet>& set)
+{
+    ResourceDirIterator it(set, String8("drawable"));
+    Vector<sp<AaptFile> > newNameFiles;
+    Vector<String8> newNamePaths;
+    ssize_t res;
+    while ((res=it.next()) == NO_ERROR) {
+        res = preProcessImage(bundle, assets, it.getFile(), NULL);
+        if (res != NO_ERROR) {
+            return res;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t postProcessImages(const sp<AaptAssets>& assets,
+                           ResourceTable* table,
+                           const sp<ResourceTypeSet>& set)
+{
+    ResourceDirIterator it(set, String8("drawable"));
+    ssize_t res;
+    while ((res=it.next()) == NO_ERROR) {
+        res = postProcessImage(assets, table, it.getFile());
+        if (res != NO_ERROR) {
+            return res;
+        }
+    }
+
+    return res < NO_ERROR ? res : (status_t)NO_ERROR;
+}
+
+static void collect_files(const sp<AaptDir>& dir,
+        KeyedVector<String8, sp<ResourceTypeSet> >* resources)
+{
+    const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
+    int N = groups.size();
+    for (int i=0; i<N; i++) {
+        String8 leafName = groups.keyAt(i);
+        const sp<AaptGroup>& group = groups.valueAt(i);
+
+        const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
+                = group->getFiles();
+
+        if (files.size() == 0) {
+            continue;
+        }
+
+        String8 resType = files.valueAt(0)->getResourceType();
+
+        ssize_t index = resources->indexOfKey(resType);
+
+        if (index < 0) {
+            sp<ResourceTypeSet> set = new ResourceTypeSet();
+            set->add(leafName, group);
+            resources->add(resType, set);
+        } else {
+            sp<ResourceTypeSet> set = resources->valueAt(index);
+            index = set->indexOfKey(leafName);
+            if (index < 0) {
+                set->add(leafName, group);
+            } else {
+                sp<AaptGroup> existingGroup = set->valueAt(index);
+                int M = files.size();
+                for (int j=0; j<M; j++) {
+                    existingGroup->addFile(files.valueAt(j));
+                }
+            }
+        }
+    }
+}
+
+static void collect_files(const sp<AaptAssets>& ass,
+        KeyedVector<String8, sp<ResourceTypeSet> >* resources)
+{
+    const Vector<sp<AaptDir> >& dirs = ass->resDirs();
+    int N = dirs.size();
+
+    for (int i=0; i<N; i++) {
+        sp<AaptDir> d = dirs.itemAt(i);
+        collect_files(d, resources);
+
+        // don't try to include the res dir
+        ass->removeDir(d->getLeaf());
+    }
+}
+
+enum {
+    ATTR_OKAY = -1,
+    ATTR_NOT_FOUND = -2,
+    ATTR_LEADING_SPACES = -3,
+    ATTR_TRAILING_SPACES = -4
+};
+static int validateAttr(const String8& path, const ResXMLParser& parser,
+        const char* ns, const char* attr, const char* validChars, bool required)
+{
+    size_t len;
+
+    ssize_t index = parser.indexOfAttribute(ns, attr);
+    const uint16_t* str;
+    if (index >= 0 && (str=parser.getAttributeStringValue(index, &len)) != NULL) {
+        if (validChars) {
+            for (size_t i=0; i<len; i++) {
+                uint16_t c = str[i];
+                const char* p = validChars;
+                bool okay = false;
+                while (*p) {
+                    if (c == *p) {
+                        okay = true;
+                        break;
+                    }
+                    p++;
+                }
+                if (!okay) {
+                    fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
+                            path.string(), parser.getLineNumber(),
+                            String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
+                    return (int)i;
+                }
+            }
+        }
+        if (*str == ' ') {
+            fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
+                    path.string(), parser.getLineNumber(),
+                    String8(parser.getElementName(&len)).string(), attr);
+            return ATTR_LEADING_SPACES;
+        }
+        if (str[len-1] == ' ') {
+            fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
+                    path.string(), parser.getLineNumber(),
+                    String8(parser.getElementName(&len)).string(), attr);
+            return ATTR_TRAILING_SPACES;
+        }
+        return ATTR_OKAY;
+    }
+    if (required) {
+        fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
+                path.string(), parser.getLineNumber(),
+                String8(parser.getElementName(&len)).string(), attr);
+        return ATTR_NOT_FOUND;
+    }
+    return ATTR_OKAY;
+}
+
+static void checkForIds(const String8& path, ResXMLParser& parser)
+{
+    ResXMLTree::event_code_t code;
+    while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
+           && code > ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::START_TAG) {
+            ssize_t index = parser.indexOfAttribute(NULL, "id");
+            if (index >= 0) {
+                fprintf(stderr, "%s:%d: WARNING: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
+                        path.string(), parser.getLineNumber());
+            }
+        }
+    }
+}
+
+static void applyFileOverlay(const sp<AaptAssets>& assets, 
+                             const sp<ResourceTypeSet>& baseSet,
+                             const char *resType)
+{
+    // Replace any base level files in this category with any found from the overlay
+    // Also add any found only in the overlay.
+    sp<AaptAssets> overlay = assets->getOverlay();
+    String8 resTypeString(resType);
+    
+    // work through the linked list of overlays
+    while (overlay.get()) {
+        KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
+
+        // get the overlay resources of the requested type
+        ssize_t index = overlayRes->indexOfKey(resTypeString);
+        if (index >= 0) {
+            sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
+
+            // for each of the resources, check for a match in the previously built
+            // non-overlay "baseset".
+            size_t overlayCount = overlaySet->size();
+            for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
+                size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
+                if (baseIndex != UNKNOWN_ERROR) {
+                    // look for same flavor.  For a given file (strings.xml, for example)
+                    // there may be a locale specific or other flavors - we want to match
+                    // the same flavor.
+                    sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
+                    sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
+                   
+                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 
+                            baseGroup->getFiles();
+                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 
+                            overlayGroup->getFiles();
+                    size_t overlayGroupSize = overlayFiles.size();
+                    for (size_t overlayGroupIndex = 0; 
+                            overlayGroupIndex<overlayGroupSize; 
+                            overlayGroupIndex++) {
+                        size_t baseFileIndex = 
+                                baseFiles.indexOfKey(overlayFiles.keyAt(overlayGroupIndex));
+                        if(baseFileIndex < UNKNOWN_ERROR) {
+                            baseGroup->removeFile(baseFileIndex);
+                        } else {
+                            // didn't find a match fall through and add it..
+                        }
+                        baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
+                    }
+                } else {
+                    // this group doesn't exist (a file that's only in the overlay)
+                    // add it
+                    baseSet->add(overlaySet->keyAt(overlayIndex),
+                                 overlaySet->valueAt(overlayIndex));
+                }
+            }
+            // this overlay didn't have resources for this type
+        }
+        // try next overlay
+        overlay = overlay->getOverlay();
+    }
+    return;
+}
+
+#define ASSIGN_IT(n) \
+        do { \
+            ssize_t index = resources->indexOfKey(String8(#n)); \
+            if (index >= 0) { \
+                n ## s = resources->valueAt(index); \
+            } \
+        } while (0)
+
+status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+    // First, look for a package file to parse.  This is required to
+    // be able to generate the resource information.
+    sp<AaptGroup> androidManifestFile =
+            assets->getFiles().valueFor(String8("AndroidManifest.xml"));
+    if (androidManifestFile == NULL) {
+        fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = parsePackage(assets, androidManifestFile);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    NOISY(printf("Creating resources for package %s\n",
+                 assets->getPackage().string()));
+
+    ResourceTable table(bundle, String16(assets->getPackage()));
+    err = table.addIncludedResources(bundle, assets);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    NOISY(printf("Found %d included resource packages\n", (int)table.size()));
+
+    // --------------------------------------------------------------
+    // First, gather all resource information.
+    // --------------------------------------------------------------
+
+    // resType -> leafName -> group
+    KeyedVector<String8, sp<ResourceTypeSet> > *resources = 
+            new KeyedVector<String8, sp<ResourceTypeSet> >;
+    collect_files(assets, resources);
+
+    sp<ResourceTypeSet> drawables;
+    sp<ResourceTypeSet> layouts;
+    sp<ResourceTypeSet> anims;
+    sp<ResourceTypeSet> xmls;
+    sp<ResourceTypeSet> raws;
+    sp<ResourceTypeSet> colors;
+    sp<ResourceTypeSet> menus;
+
+    ASSIGN_IT(drawable);
+    ASSIGN_IT(layout);
+    ASSIGN_IT(anim);
+    ASSIGN_IT(xml);
+    ASSIGN_IT(raw);
+    ASSIGN_IT(color);
+    ASSIGN_IT(menu);
+
+    assets->setResources(resources);
+    // now go through any resource overlays and collect their files
+    sp<AaptAssets> current = assets->getOverlay();
+    while(current.get()) {
+        KeyedVector<String8, sp<ResourceTypeSet> > *resources = 
+                new KeyedVector<String8, sp<ResourceTypeSet> >;
+        current->setResources(resources);
+        collect_files(current, resources);
+        current = current->getOverlay();
+    }
+    // apply the overlay files to the base set
+    applyFileOverlay(assets, drawables, "drawable");
+    applyFileOverlay(assets, layouts, "layout");
+    applyFileOverlay(assets, anims, "anim");
+    applyFileOverlay(assets, xmls, "xml");
+    applyFileOverlay(assets, raws, "raw");
+    applyFileOverlay(assets, colors, "color");
+    applyFileOverlay(assets, menus, "menu");
+
+    bool hasErrors = false;
+
+    if (drawables != NULL) {
+        err = preProcessImages(bundle, assets, drawables);
+        if (err == NO_ERROR) {
+            err = makeFileResources(bundle, assets, &table, drawables, "drawable");
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        } else {
+            hasErrors = true;
+        }
+    }
+
+    if (layouts != NULL) {
+        err = makeFileResources(bundle, assets, &table, layouts, "layout");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (anims != NULL) {
+        err = makeFileResources(bundle, assets, &table, anims, "anim");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (xmls != NULL) {
+        err = makeFileResources(bundle, assets, &table, xmls, "xml");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (raws != NULL) {
+        err = makeFileResources(bundle, assets, &table, raws, "raw");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    // compile resources
+    current = assets;
+    while(current.get()) {
+        KeyedVector<String8, sp<ResourceTypeSet> > *resources = 
+                current->getResources();
+
+        ssize_t index = resources->indexOfKey(String8("values"));
+        if (index >= 0) {
+            ResourceDirIterator it(resources->valueAt(index), String8("values"));
+            ssize_t res;
+            while ((res=it.next()) == NO_ERROR) {
+                sp<AaptFile> file = it.getFile();
+                res = compileResourceFile(bundle, assets, file, it.getParams(), 
+                                          (current!=assets), &table);
+                if (res != NO_ERROR) {
+                    hasErrors = true;
+                }
+            }
+        }
+        current = current->getOverlay();
+    }
+
+    if (colors != NULL) {
+        err = makeFileResources(bundle, assets, &table, colors, "color");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (menus != NULL) {
+        err = makeFileResources(bundle, assets, &table, menus, "menu");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    // --------------------------------------------------------------------
+    // Assignment of resource IDs and initial generation of resource table.
+    // --------------------------------------------------------------------
+
+    if (table.hasResources()) {
+        sp<AaptFile> resFile(getResourceFile(assets));
+        if (resFile == NULL) {
+            fprintf(stderr, "Error: unable to generate entry for resource data\n");
+            return UNKNOWN_ERROR;
+        }
+
+        err = table.assignResourceIds();
+        if (err < NO_ERROR) {
+            return err;
+        }
+    }
+
+    // --------------------------------------------------------------
+    // Finally, we can now we can compile XML files, which may reference
+    // resources.
+    // --------------------------------------------------------------
+
+    if (layouts != NULL) {
+        ResourceDirIterator it(layouts, String8("layout"));
+        while ((err=it.next()) == NO_ERROR) {
+            String8 src = it.getFile()->getPrintableSource();
+            err = compileXmlFile(assets, it.getFile(), &table);
+            if (err == NO_ERROR) {
+                ResXMLTree block;
+                block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
+                checkForIds(src, block);
+            } else {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    if (anims != NULL) {
+        ResourceDirIterator it(anims, String8("anim"));
+        while ((err=it.next()) == NO_ERROR) {
+            err = compileXmlFile(assets, it.getFile(), &table);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    if (xmls != NULL) {
+        ResourceDirIterator it(xmls, String8("xml"));
+        while ((err=it.next()) == NO_ERROR) {
+            err = compileXmlFile(assets, it.getFile(), &table);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    if (drawables != NULL) {
+        err = postProcessImages(assets, &table, drawables);
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (colors != NULL) {
+        ResourceDirIterator it(colors, String8("color"));
+        while ((err=it.next()) == NO_ERROR) {
+          err = compileXmlFile(assets, it.getFile(), &table);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    if (menus != NULL) {
+        ResourceDirIterator it(menus, String8("menu"));
+        while ((err=it.next()) == NO_ERROR) {
+            String8 src = it.getFile()->getPrintableSource();
+            err = compileXmlFile(assets, it.getFile(), &table);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+            ResXMLTree block;
+            block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
+            checkForIds(src, block);
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
+    String8 manifestPath(manifestFile->getPrintableSource());
+
+    // Perform a basic validation of the manifest file.  This time we
+    // parse it with the comments intact, so that we can use them to
+    // generate java docs...  so we are not going to write this one
+    // back out to the final manifest data.
+    err = compileXmlFile(assets, manifestFile, &table,
+            XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
+            | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
+    if (err < NO_ERROR) {
+        return err;
+    }
+    ResXMLTree block;
+    block.setTo(manifestFile->getData(), manifestFile->getSize(), true);
+    String16 manifest16("manifest");
+    String16 permission16("permission");
+    String16 permission_group16("permission-group");
+    String16 uses_permission16("uses-permission");
+    String16 instrumentation16("instrumentation");
+    String16 application16("application");
+    String16 provider16("provider");
+    String16 service16("service");
+    String16 receiver16("receiver");
+    String16 activity16("activity");
+    String16 action16("action");
+    String16 category16("category");
+    String16 data16("scheme");
+    const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
+    const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
+    const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
+    const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
+    const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
+    const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
+    const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
+    ResXMLTree::event_code_t code;
+    sp<AaptSymbols> permissionSymbols;
+    sp<AaptSymbols> permissionGroupSymbols;
+    while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+           && code > ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::START_TAG) {
+            size_t len;
+            if (block.getElementNamespace(&len) != NULL) {
+                continue;
+            }
+            if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
+                if (validateAttr(manifestPath, block, NULL, "package",
+                                 packageIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
+                    || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
+                const bool isGroup = strcmp16(block.getElementName(&len),
+                        permission_group16.string()) == 0;
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 isGroup ? packageIdentCharsWithTheStupid
+                                 : packageIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                SourcePos srcPos(manifestPath, block.getLineNumber());
+                sp<AaptSymbols> syms;
+                if (!isGroup) {
+                    syms = permissionSymbols;
+                    if (syms == NULL) {
+                        sp<AaptSymbols> symbols =
+                                assets->getSymbolsFor(String8("Manifest"));
+                        syms = permissionSymbols = symbols->addNestedSymbol(
+                                String8("permission"), srcPos);
+                    }
+                } else {
+                    syms = permissionGroupSymbols;
+                    if (syms == NULL) {
+                        sp<AaptSymbols> symbols =
+                                assets->getSymbolsFor(String8("Manifest"));
+                        syms = permissionGroupSymbols = symbols->addNestedSymbol(
+                                String8("permission_group"), srcPos);
+                    }
+                }
+                size_t len;
+                ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
+                const uint16_t* id = block.getAttributeStringValue(index, &len);
+                if (id == NULL) {
+                    fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 
+                            manifestPath.string(), block.getLineNumber(),
+                            String8(block.getElementName(&len)).string());
+                    hasErrors = true;
+                    break;
+                }
+                String8 idStr(id);
+                char* p = idStr.lockBuffer(idStr.size());
+                char* e = p + idStr.size();
+                bool begins_with_digit = true;  // init to true so an empty string fails
+                while (e > p) {
+                    e--;
+                    if (*e >= '0' && *e <= '9') {
+                      begins_with_digit = true;
+                      continue;
+                    }
+                    if ((*e >= 'a' && *e <= 'z') ||
+                        (*e >= 'A' && *e <= 'Z') ||
+                        (*e == '_')) {
+                      begins_with_digit = false;
+                      continue;
+                    }
+                    if (isGroup && (*e == '-')) {
+                        *e = '_';
+                        begins_with_digit = false;
+                        continue;
+                    }
+                    e++;
+                    break;
+                }
+                idStr.unlockBuffer();
+                // verify that we stopped because we hit a period or
+                // the beginning of the string, and that the
+                // identifier didn't begin with a digit.
+                if (begins_with_digit || (e != p && *(e-1) != '.')) {
+                  fprintf(stderr,
+                          "%s:%d: Permission name <%s> is not a valid Java symbol\n",
+                          manifestPath.string(), block.getLineNumber(), idStr.string());
+                  hasErrors = true;
+                }
+                syms->addStringSymbol(String8(e), idStr, srcPos);
+                const uint16_t* cmt = block.getComment(&len);
+                if (cmt != NULL && *cmt != 0) {
+                    //printf("Comment of %s: %s\n", String8(e).string(),
+                    //        String8(cmt).string());
+                    syms->appendComment(String8(e), String16(cmt), srcPos);
+                } else {
+                    //printf("No comment for %s\n", String8(e).string());
+                }
+                syms->makeSymbolPublic(String8(e), srcPos);
+            } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 packageIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 classIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "targetPackage",
+                                 packageIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 classIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "permission",
+                                 packageIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "process",
+                                 processIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
+                                 processIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 classIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "authorities",
+                                 authoritiesIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "permission",
+                                 packageIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "process",
+                                 processIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
+                       || strcmp16(block.getElementName(&len), receiver16.string()) == 0
+                       || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
+                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
+                                 classIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "permission",
+                                 packageIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "process",
+                                 processIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
+                                 processIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
+                       || strcmp16(block.getElementName(&len), category16.string()) == 0) {
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "name",
+                                 packageIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "mimeType",
+                                 typeIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+                if (validateAttr(manifestPath, block,
+                                 RESOURCES_ANDROID_NAMESPACE, "scheme",
+                                 schemeIdentChars, true) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
+            }
+        }
+    }
+
+    if (table.validateLocalizations()) {
+        hasErrors = true;
+    }
+    
+    if (hasErrors) {
+        return UNKNOWN_ERROR;
+    }
+
+    // Generate final compiled manifest file.
+    manifestFile->clearData();
+    err = compileXmlFile(assets, manifestFile, &table);
+    if (err < NO_ERROR) {
+        return err;
+    }
+
+    //block.restart();
+    //printXMLBlock(&block);
+
+    // --------------------------------------------------------------
+    // Generate the final resource table.
+    // Re-flatten because we may have added new resource IDs
+    // --------------------------------------------------------------
+
+    if (table.hasResources()) {
+        sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+        err = table.addSymbols(symbols);
+        if (err < NO_ERROR) {
+            return err;
+        }
+
+        sp<AaptFile> resFile(getResourceFile(assets));
+        if (resFile == NULL) {
+            fprintf(stderr, "Error: unable to generate entry for resource data\n");
+            return UNKNOWN_ERROR;
+        }
+
+        err = table.flatten(bundle, resFile);
+        if (err < NO_ERROR) {
+            return err;
+        }
+
+        if (bundle->getPublicOutputFile()) {
+            FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
+            if (fp == NULL) {
+                fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
+                        (const char*)bundle->getPublicOutputFile(), strerror(errno));
+                return UNKNOWN_ERROR;
+            }
+            if (bundle->getVerbose()) {
+                printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
+            }
+            table.writePublicDefinitions(String16(assets->getPackage()), fp);
+        }
+
+        NOISY(
+              ResTable rt;
+              rt.add(resFile->getData(), resFile->getSize(), NULL);
+              printf("Generated resources:\n");
+              rt.print();
+        )
+
+        // These resources are now considered to be a part of the included
+        // resources, for others to reference.
+        err = assets->addIncludedResources(resFile);
+        if (err < NO_ERROR) {
+            fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
+            return err;
+        }
+    }
+
+    return err;
+}
+
+static const char* getIndentSpace(int indent)
+{
+static const char whitespace[] =
+"                                                                                       ";
+
+    return whitespace + sizeof(whitespace) - 1 - indent*4;
+}
+
+static status_t fixupSymbol(String16* inoutSymbol)
+{
+    inoutSymbol->replaceAll('.', '_');
+    inoutSymbol->replaceAll(':', '_');
+    return NO_ERROR;
+}
+
+static String16 getAttributeComment(const sp<AaptAssets>& assets,
+                                    const String8& name,
+                                    String16* outTypeComment = NULL)
+{
+    sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
+    if (asym != NULL) {
+        //printf("Got R symbols!\n");
+        asym = asym->getNestedSymbols().valueFor(String8("attr"));
+        if (asym != NULL) {
+            //printf("Got attrs symbols! comment %s=%s\n",
+            //     name.string(), String8(asym->getComment(name)).string());
+            if (outTypeComment != NULL) {
+                *outTypeComment = asym->getTypeComment(name);
+            }
+            return asym->getComment(name);
+        }
+    }
+    return String16();
+}
+
+static status_t writeLayoutClasses(
+    FILE* fp, const sp<AaptAssets>& assets,
+    const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
+{
+    const char* indentStr = getIndentSpace(indent);
+    if (!includePrivate) {
+        fprintf(fp, "%s/** @doconly */\n", indentStr);
+    }
+    fprintf(fp, "%spublic static final class styleable {\n", indentStr);
+    indent++;
+
+    String16 attr16("attr");
+    String16 package16(assets->getPackage());
+
+    indentStr = getIndentSpace(indent);
+    bool hasErrors = false;
+
+    size_t i;
+    size_t N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
+        String8 realClassName(nclassName16);
+        if (fixupSymbol(&nclassName16) != NO_ERROR) {
+            hasErrors = true;
+        }
+        String8 nclassName(nclassName16);
+
+        SortedVector<uint32_t> idents;
+        Vector<uint32_t> origOrder;
+        Vector<bool> publicFlags;
+
+        size_t a;
+        size_t NA = nsymbols->getSymbols().size();
+        for (a=0; a<NA; a++) {
+            const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
+            int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
+                    ? sym.int32Val : 0;
+            bool isPublic = true;
+            if (code == 0) {
+                String16 name16(sym.name);
+                uint32_t typeSpecFlags;
+                code = assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                if (code == 0) {
+                    fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
+                            nclassName.string(), sym.name.string());
+                    hasErrors = true;
+                }
+                isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+            }
+            idents.add(code);
+            origOrder.add(code);
+            publicFlags.add(isPublic);
+        }
+
+        NA = idents.size();
+
+        String16 comment = symbols->getComment(realClassName);
+        fprintf(fp, "%s/** ", indentStr);
+        if (comment.size() > 0) {
+            fprintf(fp, "%s\n", String8(comment).string());
+        } else {
+            fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
+        }
+        bool hasTable = false;
+        for (a=0; a<NA; a++) {
+            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+            if (pos >= 0) {
+                if (!hasTable) {
+                    hasTable = true;
+                    fprintf(fp,
+                            "%s   <p>Includes the following attributes:</p>\n"
+                            "%s   <table border=\"2\" width=\"85%%\" align=\"center\" frame=\"hsides\" rules=\"all\" cellpadding=\"5\">\n"
+                            "%s   <colgroup align=\"left\" />\n"
+                            "%s   <colgroup align=\"left\" />\n"
+                            "%s   <tr><th>Attribute<th>Summary</tr>\n",
+                            indentStr,
+                            indentStr,
+                            indentStr,
+                            indentStr,
+                            indentStr);
+                }
+                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+                if (!publicFlags.itemAt(a) && !includePrivate) {
+                    continue;
+                }
+                String8 name8(sym.name);
+                String16 comment(sym.comment);
+                if (comment.size() <= 0) {
+                    comment = getAttributeComment(assets, name8);
+                }
+                if (comment.size() > 0) {
+                    const char16_t* p = comment.string();
+                    while (*p != 0 && *p != '.') {
+                        if (*p == '{') {
+                            while (*p != 0 && *p != '}') {
+                                p++;
+                            }
+                        } else {
+                            p++;
+                        }
+                    }
+                    if (*p == '.') {
+                        p++;
+                    }
+                    comment = String16(comment.string(), p-comment.string());
+                }
+                String16 name(name8);
+                fixupSymbol(&name);
+                fprintf(fp, "%s   <tr><th><code>{@link #%s_%s %s:%s}</code><td>%s</tr>\n",
+                        indentStr, nclassName.string(),
+                        String8(name).string(),
+                        assets->getPackage().string(),
+                        String8(name).string(),
+                        String8(comment).string());
+            }
+        }
+        if (hasTable) {
+            fprintf(fp, "%s   </table>\n", indentStr);
+        }
+        for (a=0; a<NA; a++) {
+            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+            if (pos >= 0) {
+                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+                if (!publicFlags.itemAt(a) && !includePrivate) {
+                    continue;
+                }
+                String16 name(sym.name);
+                fixupSymbol(&name);
+                fprintf(fp, "%s   @see #%s_%s\n",
+                        indentStr, nclassName.string(),
+                        String8(name).string());
+            }
+        }
+        fprintf(fp, "%s */\n", getIndentSpace(indent));
+
+        fprintf(fp,
+                "%spublic static final int[] %s = {\n"
+                "%s",
+                indentStr, nclassName.string(),
+                getIndentSpace(indent+1));
+
+        for (a=0; a<NA; a++) {
+            if (a != 0) {
+                if ((a&3) == 0) {
+                    fprintf(fp, ",\n%s", getIndentSpace(indent+1));
+                } else {
+                    fprintf(fp, ", ");
+                }
+            }
+            fprintf(fp, "0x%08x", idents[a]);
+        }
+
+        fprintf(fp, "\n%s};\n", indentStr);
+
+        for (a=0; a<NA; a++) {
+            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+            if (pos >= 0) {
+                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+                if (!publicFlags.itemAt(a) && !includePrivate) {
+                    continue;
+                }
+                String8 name8(sym.name);
+                String16 comment(sym.comment);
+                String16 typeComment;
+                if (comment.size() <= 0) {
+                    comment = getAttributeComment(assets, name8, &typeComment);
+                } else {
+                    getAttributeComment(assets, name8, &typeComment);
+                }
+                String16 name(name8);
+                if (fixupSymbol(&name) != NO_ERROR) {
+                    hasErrors = true;
+                }
+                
+                uint32_t typeSpecFlags = 0;
+                String16 name16(sym.name);
+                assets->getIncludedResources().identifierForName(
+                    name16.string(), name16.size(),
+                    attr16.string(), attr16.size(),
+                    package16.string(), package16.size(), &typeSpecFlags);
+                //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
+                //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+                const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+                    
+                fprintf(fp, "%s/**\n", indentStr);
+                if (comment.size() > 0) {
+                    fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
+                    fprintf(fp, "%s  %s\n", indentStr, String8(comment).string());
+                } else {
+                    fprintf(fp,
+                            "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
+                            "%s  attribute's value can be found in the {@link #%s} array.\n",
+                            indentStr,
+                            pub ? assets->getPackage().string()
+                                : assets->getSymbolsPrivatePackage().string(),
+                            String8(name).string(),
+                            indentStr, nclassName.string());
+                }
+                if (typeComment.size() > 0) {
+                    fprintf(fp, "\n\n%s  %s\n", indentStr, String8(typeComment).string());
+                }
+                if (comment.size() > 0) {
+                    if (pub) {
+                        fprintf(fp,
+                                "%s  <p>This corresponds to the global attribute"
+                                "%s  resource symbol {@link %s.R.attr#%s}.\n",
+                                indentStr, indentStr,
+                                assets->getPackage().string(),
+                                String8(name).string());
+                    } else {
+                        fprintf(fp,
+                                "%s  <p>This is a private symbol.\n", indentStr);
+                    }
+                }
+                fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
+                        "android", String8(name).string());
+                fprintf(fp, "%s*/\n", indentStr);
+                fprintf(fp,
+                        "%spublic static final int %s_%s = %d;\n",
+                        indentStr, nclassName.string(),
+                        String8(name).string(), (int)pos);
+            }
+        }
+    }
+
+    indent--;
+    fprintf(fp, "%s};\n", getIndentSpace(indent));
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+static status_t writeSymbolClass(
+    FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
+    const sp<AaptSymbols>& symbols, const String8& className, int indent)
+{
+    fprintf(fp, "%spublic %sfinal class %s {\n",
+            getIndentSpace(indent),
+            indent != 0 ? "static " : "", className.string());
+    indent++;
+
+    size_t i;
+    status_t err = NO_ERROR;
+
+    size_t N = symbols->getSymbols().size();
+    for (i=0; i<N; i++) {
+        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+        if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
+            continue;
+        }
+        if (!includePrivate && !sym.isPublic) {
+            continue;
+        }
+        String16 name(sym.name);
+        String8 realName(name);
+        if (fixupSymbol(&name) != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+        String16 comment(sym.comment);
+        bool haveComment = false;
+        if (comment.size() > 0) {
+            haveComment = true;
+            fprintf(fp,
+                    "%s/** %s\n",
+                    getIndentSpace(indent), String8(comment).string());
+        } else if (sym.isPublic && !includePrivate) {
+            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
+                assets->getPackage().string(), className.string(),
+                String8(sym.name).string());
+        }
+        String16 typeComment(sym.typeComment);
+        if (typeComment.size() > 0) {
+            if (!haveComment) {
+                haveComment = true;
+                fprintf(fp,
+                        "%s/** %s\n",
+                        getIndentSpace(indent), String8(typeComment).string());
+            } else {
+                fprintf(fp,
+                        "%s %s\n",
+                        getIndentSpace(indent), String8(typeComment).string());
+            }
+        }
+        if (haveComment) {
+            fprintf(fp,"%s */\n", getIndentSpace(indent));
+        }
+        fprintf(fp, "%spublic static final int %s=0x%08x;\n",
+                getIndentSpace(indent),
+                String8(name).string(), (int)sym.int32Val);
+    }
+
+    for (i=0; i<N; i++) {
+        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+        if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
+            continue;
+        }
+        if (!includePrivate && !sym.isPublic) {
+            continue;
+        }
+        String16 name(sym.name);
+        if (fixupSymbol(&name) != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+        String16 comment(sym.comment);
+        if (comment.size() > 0) {
+            fprintf(fp,
+                    "%s/** %s\n"
+                     "%s */\n",
+                    getIndentSpace(indent), String8(comment).string(),
+                    getIndentSpace(indent));
+        } else if (sym.isPublic && !includePrivate) {
+            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
+                assets->getPackage().string(), className.string(),
+                String8(sym.name).string());
+        }
+        fprintf(fp, "%spublic static final String %s=\"%s\";\n",
+                getIndentSpace(indent),
+                String8(name).string(), sym.stringVal.string());
+    }
+
+    sp<AaptSymbols> styleableSymbols;
+
+    N = symbols->getNestedSymbols().size();
+    for (i=0; i<N; i++) {
+        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+        String8 nclassName(symbols->getNestedSymbols().keyAt(i));
+        if (nclassName == "styleable") {
+            styleableSymbols = nsymbols;
+        } else {
+            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent);
+        }
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    if (styleableSymbols != NULL) {
+        err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    indent--;
+    fprintf(fp, "%s}\n", getIndentSpace(indent));
+    return NO_ERROR;
+}
+
+status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
+    const String8& package, bool includePrivate)
+{
+    if (!bundle->getRClassDir()) {
+        return NO_ERROR;
+    }
+
+    const size_t N = assets->getSymbols().size();
+    for (size_t i=0; i<N; i++) {
+        sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
+        String8 className(assets->getSymbols().keyAt(i));
+        String8 dest(bundle->getRClassDir());
+        if (bundle->getMakePackageDirs()) {
+            String8 pkg(package);
+            const char* last = pkg.string();
+            const char* s = last-1;
+            do {
+                s++;
+                if (s > last && (*s == '.' || *s == 0)) {
+                    String8 part(last, s-last);
+                    dest.appendPath(part);
+#ifdef HAVE_MS_C_RUNTIME
+                    _mkdir(dest.string());
+#else
+                    mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+                    last = s+1;
+                }
+            } while (*s);
+        }
+        dest.appendPath(className);
+        dest.append(".java");
+        FILE* fp = fopen(dest.string(), "w+");
+        if (fp == NULL) {
+            fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
+                    dest.string(), strerror(errno));
+            return UNKNOWN_ERROR;
+        }
+        if (bundle->getVerbose()) {
+            printf("  Writing symbols for class %s.\n", className.string());
+        }
+
+        fprintf(fp,
+        "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
+        " *\n"
+        " * This class was automatically generated by the\n"
+        " * aapt tool from the resource data it found.  It\n"
+        " * should not be modified by hand.\n"
+        " */\n"
+        "\n"
+        "package %s;\n\n", package.string());
+
+        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        fclose(fp);
+    }
+
+    return NO_ERROR;
+}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
new file mode 100644
index 0000000..6f71a1e
--- /dev/null
+++ b/tools/aapt/ResourceTable.cpp
@@ -0,0 +1,3491 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "ResourceTable.h"
+
+#include "XMLNode.h"
+
+#include <utils/ByteOrder.h>
+#include <utils/ResourceTypes.h>
+#include <stdarg.h>
+
+#define NOISY(x) //x
+
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+                        const sp<AaptFile>& target,
+                        ResourceTable* table,
+                        int options)
+{
+    sp<XMLNode> root = XMLNode::parse(target);
+    if (root == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
+        root->removeWhitespace(true, NULL);
+    } else  if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
+        root->removeWhitespace(false, NULL);
+    }
+
+    bool hasErrors = false;
+    
+    if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
+        status_t err = root->assignResourceIds(assets, table);
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    status_t err = root->parseValues(assets, table);
+    if (err != NO_ERROR) {
+        hasErrors = true;
+    }
+
+    if (hasErrors) {
+        return UNKNOWN_ERROR;
+    }
+    
+    NOISY(printf("Input XML Resource:\n"));
+    NOISY(root->print());
+    err = root->flatten(target,
+            (options&XML_COMPILE_STRIP_COMMENTS) != 0,
+            (options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    NOISY(printf("Output XML Resource:\n"));
+    NOISY(ResXMLTree tree;
+        tree.setTo(target->getData(), target->getSize());
+        printXMLBlock(&tree));
+
+    target->setCompressionMethod(ZipEntry::kCompressDeflated);
+    
+    return err;
+}
+
+#undef NOISY
+#define NOISY(x) //x
+
+struct flag_entry
+{
+    const char16_t* name;
+    size_t nameLen;
+    uint32_t value;
+    const char* description;
+};
+
+static const char16_t referenceArray[] =
+    { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' };
+static const char16_t stringArray[] =
+    { 's', 't', 'r', 'i', 'n', 'g' };
+static const char16_t integerArray[] =
+    { 'i', 'n', 't', 'e', 'g', 'e', 'r' };
+static const char16_t booleanArray[] =
+    { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
+static const char16_t colorArray[] =
+    { 'c', 'o', 'l', 'o', 'r' };
+static const char16_t floatArray[] =
+    { 'f', 'l', 'o', 'a', 't' };
+static const char16_t dimensionArray[] =
+    { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' };
+static const char16_t fractionArray[] =
+    { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' };
+static const char16_t enumArray[] =
+    { 'e', 'n', 'u', 'm' };
+static const char16_t flagsArray[] =
+    { 'f', 'l', 'a', 'g', 's' };
+
+static const flag_entry gFormatFlags[] = {
+    { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE,
+      "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n"
+      "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."},
+    { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING,
+      "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." },
+    { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER,
+      "an integer value, such as \"<code>100</code>\"." },
+    { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN,
+      "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." },
+    { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR,
+      "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n"
+      "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." },
+    { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT,
+      "a floating point value, such as \"<code>1.2</code>\"."},
+    { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
+      "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
+      "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
+      "in (inches), mm (millimeters)." },
+    { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
+      "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
+      "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n"
+      "some parent container." },
+    { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL },
+    { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL },
+    { NULL, 0, 0, NULL }
+};
+
+static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
+
+static const flag_entry l10nRequiredFlags[] = {
+    { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
+    { NULL, 0, 0, NULL }
+};
+
+static const char16_t nulStr[] = { 0 };
+
+static uint32_t parse_flags(const char16_t* str, size_t len,
+                             const flag_entry* flags, bool* outError = NULL)
+{
+    while (len > 0 && isspace(*str)) {
+        str++;
+        len--;
+    }
+    while (len > 0 && isspace(str[len-1])) {
+        len--;
+    }
+
+    const char16_t* const end = str + len;
+    uint32_t value = 0;
+
+    while (str < end) {
+        const char16_t* div = str;
+        while (div < end && *div != '|') {
+            div++;
+        }
+
+        const flag_entry* cur = flags;
+        while (cur->name) {
+            if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
+                value |= cur->value;
+                break;
+            }
+            cur++;
+        }
+
+        if (!cur->name) {
+            if (outError) *outError = true;
+            return 0;
+        }
+
+        str = div < end ? div+1 : div;
+    }
+
+    if (outError) *outError = false;
+    return value;
+}
+
+static String16 mayOrMust(int type, int flags)
+{
+    if ((type&(~flags)) == 0) {
+        return String16("<p>Must");
+    }
+    
+    return String16("<p>May");
+}
+
+static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
+        const String16& typeName, const String16& ident, int type,
+        const flag_entry* flags)
+{
+    bool hadType = false;
+    while (flags->name) {
+        if ((type&flags->value) != 0 && flags->description != NULL) {
+            String16 fullMsg(mayOrMust(type, flags->value));
+            fullMsg.append(String16(" be "));
+            fullMsg.append(String16(flags->description));
+            outTable->appendTypeComment(pkg, typeName, ident, fullMsg);
+            hadType = true;
+        }
+        flags++;
+    }
+    if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) {
+        outTable->appendTypeComment(pkg, typeName, ident,
+                String16("<p>This may also be a reference to a resource (in the form\n"
+                         "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n"
+                         "theme attribute (in the form\n"
+                         "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n"
+                         "containing a value of this type."));
+    }
+}
+
+struct PendingAttribute
+{
+    const String16 myPackage;
+    const SourcePos sourcePos;
+    const bool appendComment;
+    int32_t type;
+    String16 ident;
+    String16 comment;
+    bool hasErrors;
+    bool added;
+    
+    PendingAttribute(String16 _package, const sp<AaptFile>& in,
+            ResXMLTree& block, bool _appendComment)
+        : myPackage(_package)
+        , sourcePos(in->getPrintableSource(), block.getLineNumber())
+        , appendComment(_appendComment)
+        , type(ResTable_map::TYPE_ANY)
+        , hasErrors(false)
+        , added(false)
+    {
+    }
+    
+    status_t createIfNeeded(ResourceTable* outTable)
+    {
+        if (added || hasErrors) {
+            return NO_ERROR;
+        }
+        added = true;
+
+        String16 attr16("attr");
+        
+        if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
+            sourcePos.error("Attribute \"%s\" has already been defined\n",
+                    String8(ident).string());
+            hasErrors = true;
+            return UNKNOWN_ERROR;
+        }
+        
+        char numberStr[16];
+        sprintf(numberStr, "%d", type);
+        status_t err = outTable->addBag(sourcePos, myPackage,
+                attr16, ident, String16(""),
+                String16("^type"),
+                String16(numberStr), NULL, NULL);
+        if (err != NO_ERROR) {
+            hasErrors = true;
+            return err;
+        }
+        outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
+        //printf("Attribute %s comment: %s\n", String8(ident).string(),
+        //     String8(comment).string());
+        return err;
+    }
+};
+
+static status_t compileAttribute(const sp<AaptFile>& in,
+                                 ResXMLTree& block,
+                                 const String16& myPackage,
+                                 ResourceTable* outTable,
+                                 String16* outIdent = NULL,
+                                 bool inStyleable = false)
+{
+    PendingAttribute attr(myPackage, in, block, inStyleable);
+    
+    const String16 attr16("attr");
+    const String16 id16("id");
+
+    // Attribute type constants.
+    const String16 enum16("enum");
+    const String16 flag16("flag");
+
+    ResXMLTree::event_code_t code;
+    size_t len;
+    status_t err;
+    
+    ssize_t identIdx = block.indexOfAttribute(NULL, "name");
+    if (identIdx >= 0) {
+        attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
+        if (outIdent) {
+            *outIdent = attr.ident;
+        }
+    } else {
+        attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
+        attr.hasErrors = true;
+    }
+
+    attr.comment = String16(
+            block.getComment(&len) ? block.getComment(&len) : nulStr);
+
+    ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
+    if (typeIdx >= 0) {
+        String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
+        attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
+        if (attr.type == 0) {
+            attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
+                    String8(typeStr).string());
+            attr.hasErrors = true;
+        }
+        attr.createIfNeeded(outTable);
+    } else if (!inStyleable) {
+        // Attribute definitions outside of styleables always define the
+        // attribute as a generic value.
+        attr.createIfNeeded(outTable);
+    }
+
+    //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
+
+    ssize_t minIdx = block.indexOfAttribute(NULL, "min");
+    if (minIdx >= 0) {
+        String16 val = String16(block.getAttributeStringValue(minIdx, &len));
+        if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
+            attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
+                    String8(val).string());
+            attr.hasErrors = true;
+        }
+        attr.createIfNeeded(outTable);
+        if (!attr.hasErrors) {
+            err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
+                    String16(""), String16("^min"), String16(val), NULL, NULL);
+            if (err != NO_ERROR) {
+                attr.hasErrors = true;
+            }
+        }
+    }
+
+    ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
+    if (maxIdx >= 0) {
+        String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
+        if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
+            attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
+                    String8(val).string());
+            attr.hasErrors = true;
+        }
+        attr.createIfNeeded(outTable);
+        if (!attr.hasErrors) {
+            err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
+                    String16(""), String16("^max"), String16(val), NULL, NULL);
+            attr.hasErrors = true;
+        }
+    }
+
+    if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) {
+        attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n");
+        attr.hasErrors = true;
+    }
+
+    ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
+    if (l10nIdx >= 0) {
+        const uint16_t* str = block.getAttributeStringValue(l10nIdx, &len);
+        bool error;
+        uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
+        if (error) {
+            attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
+                    String8(str).string());
+            attr.hasErrors = true;
+        }
+        attr.createIfNeeded(outTable);
+        if (!attr.hasErrors) {
+            char buf[10];
+            sprintf(buf, "%d", l10n_required);
+            err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
+                    String16(""), String16("^l10n"), String16(buf), NULL, NULL);
+            if (err != NO_ERROR) {
+                attr.hasErrors = true;
+            }
+        }
+    }
+
+    String16 enumOrFlagsComment;
+    
+    while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::START_TAG) {
+            uint32_t localType = 0;
+            if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
+                localType = ResTable_map::TYPE_ENUM;
+            } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
+                localType = ResTable_map::TYPE_FLAGS;
+            } else {
+                SourcePos(in->getPrintableSource(), block.getLineNumber())
+                        .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
+                        String8(block.getElementName(&len)).string());
+                return UNKNOWN_ERROR;
+            }
+
+            attr.createIfNeeded(outTable);
+            
+            if (attr.type == ResTable_map::TYPE_ANY) {
+                // No type was explicitly stated, so supplying enum tags
+                // implicitly creates an enum or flag.
+                attr.type = 0;
+            }
+
+            if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) {
+                // Wasn't originally specified as an enum, so update its type.
+                attr.type |= localType;
+                if (!attr.hasErrors) {
+                    char numberStr[16];
+                    sprintf(numberStr, "%d", attr.type);
+                    err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
+                            myPackage, attr16, attr.ident, String16(""),
+                            String16("^type"), String16(numberStr), NULL, NULL, true);
+                    if (err != NO_ERROR) {
+                        attr.hasErrors = true;
+                    }
+                }
+            } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) {
+                if (localType == ResTable_map::TYPE_ENUM) {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber())
+                            .error("<enum> attribute can not be used inside a flags format\n");
+                    attr.hasErrors = true;
+                } else {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber())
+                            .error("<flag> attribute can not be used inside a enum format\n");
+                    attr.hasErrors = true;
+                }
+            }
+
+            String16 itemIdent;
+            ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
+            if (itemIdentIdx >= 0) {
+                itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
+            } else {
+                SourcePos(in->getPrintableSource(), block.getLineNumber())
+                        .error("A 'name' attribute is required for <enum> or <flag>\n");
+                attr.hasErrors = true;
+            }
+
+            String16 value;
+            ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
+            if (valueIdx >= 0) {
+                value = String16(block.getAttributeStringValue(valueIdx, &len));
+            } else {
+                SourcePos(in->getPrintableSource(), block.getLineNumber())
+                        .error("A 'value' attribute is required for <enum> or <flag>\n");
+                attr.hasErrors = true;
+            }
+            if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
+                SourcePos(in->getPrintableSource(), block.getLineNumber())
+                        .error("Tag <enum> or <flag> 'value' attribute must be a number,"
+                        " not \"%s\"\n",
+                        String8(value).string());
+                attr.hasErrors = true;
+            }
+
+            // Make sure an id is defined for this enum/flag identifier...
+            if (!attr.hasErrors && !outTable->hasBagOrEntry(itemIdent, &id16, &myPackage)) {
+                err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
+                                         myPackage, id16, itemIdent, String16(), NULL);
+                if (err != NO_ERROR) {
+                    attr.hasErrors = true;
+                }
+            }
+
+            if (!attr.hasErrors) {
+                if (enumOrFlagsComment.size() == 0) {
+                    enumOrFlagsComment.append(mayOrMust(attr.type,
+                            ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS));
+                    enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
+                                       ? String16(" be one of the following constant values.")
+                                       : String16(" be one or more (separated by '|') of the following constant values."));
+                    enumOrFlagsComment.append(String16("</p>\n<table border=\"2\" width=\"85%\" align=\"center\" frame=\"hsides\" rules=\"all\" cellpadding=\"5\">\n"
+                                                "<colgroup align=\"left\" />\n"
+                                                "<colgroup align=\"left\" />\n"
+                                                "<colgroup align=\"left\" />\n"
+                                                "<tr><th>Constant<th>Value<th>Description</tr>"));
+                }
+                
+                enumOrFlagsComment.append(String16("\n<tr><th><code>"));
+                enumOrFlagsComment.append(itemIdent);
+                enumOrFlagsComment.append(String16("</code><td>"));
+                enumOrFlagsComment.append(value);
+                enumOrFlagsComment.append(String16("<td>"));
+                if (block.getComment(&len)) {
+                    enumOrFlagsComment.append(String16(block.getComment(&len)));
+                }
+                enumOrFlagsComment.append(String16("</tr>"));
+                
+                err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
+                                       myPackage,
+                                       attr16, attr.ident, String16(""),
+                                       itemIdent, value, NULL, NULL, false, true);
+                if (err != NO_ERROR) {
+                    attr.hasErrors = true;
+                }
+            }
+        } else if (code == ResXMLTree::END_TAG) {
+            if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
+                break;
+            }
+            if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
+                if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber())
+                            .error("Found tag </%s> where </enum> is expected\n",
+                            String8(block.getElementName(&len)).string());
+                    return UNKNOWN_ERROR;
+                }
+            } else {
+                if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber())
+                            .error("Found tag </%s> where </flag> is expected\n",
+                            String8(block.getElementName(&len)).string());
+                    return UNKNOWN_ERROR;
+                }
+            }
+        }
+    }
+    
+    if (!attr.hasErrors && attr.added) {
+        appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
+    }
+    
+    if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
+        enumOrFlagsComment.append(String16("\n</table>"));
+        outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
+    }
+
+
+    return NO_ERROR;
+}
+
+bool localeIsDefined(const ResTable_config& config)
+{
+    return config.locale == 0;
+}
+
+status_t parseAndAddBag(Bundle* bundle,
+                        const sp<AaptFile>& in,
+                        ResXMLTree* block,
+                        const ResTable_config& config,
+                        const String16& myPackage,
+                        const String16& curType,
+                        const String16& ident,
+                        const String16& parentIdent,
+                        const String16& itemIdent,
+                        int32_t curFormat,
+                        bool pseudolocalize,
+                        const bool overwrite,
+                        ResourceTable* outTable)
+{
+    status_t err;
+    const String16 item16("item");
+    
+    String16 str;
+    Vector<StringPool::entry_style_span> spans;
+    err = parseStyledString(bundle, in->getPrintableSource().string(),
+                            block, item16, &str, &spans,
+                            pseudolocalize);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    
+    NOISY(printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d "
+                 " pid=%s, bag=%s, id=%s: %s\n",
+                 config.language[0], config.language[1],
+                 config.country[0], config.country[1],
+                 config.orientation, config.density,
+                 String8(parentIdent).string(),
+                 String8(ident).string(),
+                 String8(itemIdent).string(),
+                 String8(str).string()));
+
+    err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
+                           myPackage, curType, ident, parentIdent, itemIdent, str,
+                           &spans, &config, overwrite, false, curFormat);
+    return err;
+}
+
+
+status_t parseAndAddEntry(Bundle* bundle,
+                        const sp<AaptFile>& in,
+                        ResXMLTree* block,
+                        const ResTable_config& config,
+                        const String16& myPackage,
+                        const String16& curType,
+                        const String16& ident,
+                        const String16& curTag,
+                        bool curIsStyled,
+                        int32_t curFormat,
+                        bool pseudolocalize,
+                        const bool overwrite,
+                        ResourceTable* outTable)
+{
+    status_t err;
+
+    String16 str;
+    Vector<StringPool::entry_style_span> spans;
+    err = parseStyledString(bundle, in->getPrintableSource().string(), block,
+                            curTag, &str, curIsStyled ? &spans : NULL,
+                            pseudolocalize);
+
+    if (err < NO_ERROR) { 
+        return err;
+    }
+
+    NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
+                 config.language[0], config.language[1],
+                 config.country[0], config.country[1],
+                 config.orientation, config.density,
+                 String8(ident).string(), String8(str).string()));
+
+    err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
+                             myPackage, curType, ident, str, &spans, &config,
+                             false, curFormat, overwrite);
+
+    return err;
+}
+
+status_t compileResourceFile(Bundle* bundle,
+                             const sp<AaptAssets>& assets,
+                             const sp<AaptFile>& in,
+                             const ResTable_config& defParams,
+                             const bool overwrite,
+                             ResourceTable* outTable)
+{
+    ResXMLTree block;
+    status_t err = parseXMLResource(in, &block, false, true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // Top-level tag.
+    const String16 resources16("resources");
+
+    // Identifier declaration tags.
+    const String16 declare_styleable16("declare-styleable");
+    const String16 attr16("attr");
+
+    // Data creation organizational tags.
+    const String16 string16("string");
+    const String16 drawable16("drawable");
+    const String16 color16("color");
+    const String16 bool16("bool");
+    const String16 integer16("integer");
+    const String16 dimen16("dimen");
+    const String16 fraction16("fraction");
+    const String16 style16("style");
+    const String16 plurals16("plurals");
+    const String16 array16("array");
+    const String16 string_array16("string-array");
+    const String16 integer_array16("integer-array");
+    const String16 public16("public");
+    const String16 private_symbols16("private-symbols");
+    const String16 skip16("skip");
+    const String16 eat_comment16("eat-comment");
+
+    // Data creation tags.
+    const String16 bag16("bag");
+    const String16 item16("item");
+
+    // Attribute type constants.
+    const String16 enum16("enum");
+
+    // plural values
+    const String16 other16("other");
+    const String16 quantityOther16("^other");
+    const String16 zero16("zero");
+    const String16 quantityZero16("^zero");
+    const String16 one16("one");
+    const String16 quantityOne16("^one");
+    const String16 two16("two");
+    const String16 quantityTwo16("^two");
+    const String16 few16("few");
+    const String16 quantityFew16("^few");
+    const String16 many16("many");
+    const String16 quantityMany16("^many");
+
+    // useful attribute names and special values
+    const String16 name16("name");
+    const String16 translatable16("translatable");
+    const String16 false16("false");
+
+    const String16 myPackage(assets->getPackage());
+
+    bool hasErrors = false;
+    
+    uint32_t nextPublicId = 0;
+
+    ResXMLTree::event_code_t code;
+    do {
+        code = block.next();
+    } while (code == ResXMLTree::START_NAMESPACE);
+
+    size_t len;
+    if (code != ResXMLTree::START_TAG) {
+        SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                "No start tag found\n");
+        return UNKNOWN_ERROR;
+    }
+    if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
+        SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
+        return UNKNOWN_ERROR;
+    }
+
+    ResTable_config curParams(defParams);
+
+    ResTable_config pseudoParams(curParams);
+        pseudoParams.language[0] = 'z';
+        pseudoParams.language[1] = 'z';
+        pseudoParams.country[0] = 'Z';
+        pseudoParams.country[1] = 'Z';
+
+    while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::START_TAG) {
+            const String16* curTag = NULL;
+            String16 curType;
+            int32_t curFormat = ResTable_map::TYPE_ANY;
+            bool curIsBag = false;
+            bool curIsStyled = false;
+            bool curIsPseudolocalizable = false;
+            bool localHasErrors = false;
+
+            if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                        && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                        && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 type;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <public>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                type = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <public>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                uint32_t ident = 0;
+                ssize_t identIdx = block.indexOfAttribute(NULL, "id");
+                if (identIdx >= 0) {
+                    const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
+                    Res_value identValue;
+                    if (!ResTable::stringToInt(identStr, len, &identValue)) {
+                        srcPos.error("Given 'id' attribute is not an integer: %s\n",
+                                String8(block.getAttributeStringValue(identIdx, &len)).string());
+                        hasErrors = localHasErrors = true;
+                    } else {
+                        ident = identValue.data;
+                        nextPublicId = ident+1;
+                    }
+                } else if (nextPublicId == 0) {
+                    srcPos.error("No 'id' attribute supplied <public>,"
+                            " and no previous id defined in this file.\n");
+                    hasErrors = localHasErrors = true;
+                } else if (!localHasErrors) {
+                    ident = nextPublicId;
+                    nextPublicId++;
+                }
+
+                if (!localHasErrors) {
+                    err = outTable->addPublic(srcPos, myPackage, type, name, ident);
+                    if (err < NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+                if (!localHasErrors) {
+                    sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+                    if (symbols != NULL) {
+                        symbols = symbols->addNestedSymbol(String8(type), srcPos);
+                    }
+                    if (symbols != NULL) {
+                        symbols->makeSymbolPublic(String8(name), srcPos);
+                        String16 comment(
+                            block.getComment(&len) ? block.getComment(&len) : nulStr);
+                        symbols->appendComment(String8(name), comment, srcPos);
+                    } else {
+                        srcPos.error("Unable to create symbols!\n");
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
+                String16 pkg;
+                ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
+                if (pkgIdx < 0) {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                            "A 'package' attribute is required for <private-symbols>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
+                if (!localHasErrors) {
+                    assets->setSymbolsPrivatePackage(String8(pkg));
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+                                
+                String16 ident;
+                ssize_t identIdx = block.indexOfAttribute(NULL, "name");
+                if (identIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                ident = String16(block.getAttributeStringValue(identIdx, &len));
+
+                sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+                if (!localHasErrors) {
+                    if (symbols != NULL) {
+                        symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
+                    }
+                    sp<AaptSymbols> styleSymbols = symbols;
+                    if (symbols != NULL) {
+                        symbols = symbols->addNestedSymbol(String8(ident), srcPos);
+                    }
+                    if (symbols == NULL) {
+                        srcPos.error("Unable to create symbols!\n");
+                        return UNKNOWN_ERROR;
+                    }
+                    
+                    String16 comment(
+                        block.getComment(&len) ? block.getComment(&len) : nulStr);
+                    styleSymbols->appendComment(String8(ident), comment, srcPos);
+                } else {
+                    symbols = NULL;
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::START_TAG) {
+                        if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+                            while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                                   && code != ResXMLTree::BAD_DOCUMENT) {
+                                if (code == ResXMLTree::END_TAG) {
+                                    if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+                                        break;
+                                    }
+                                }
+                            }
+                            continue;
+                        } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+                            while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                                   && code != ResXMLTree::BAD_DOCUMENT) {
+                                if (code == ResXMLTree::END_TAG) {
+                                    if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+                                        break;
+                                    }
+                                }
+                            }
+                            continue;
+                        } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                    "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
+                                    String8(block.getElementName(&len)).string());
+                            return UNKNOWN_ERROR;
+                        }
+
+                        String16 comment(
+                            block.getComment(&len) ? block.getComment(&len) : nulStr);
+                        String16 itemIdent;
+                        err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
+                        if (err != NO_ERROR) {
+                            hasErrors = localHasErrors = true;
+                        }
+
+                        if (symbols != NULL) {
+                            SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
+                            symbols->addSymbol(String8(itemIdent), 0, srcPos);
+                            symbols->appendComment(String8(itemIdent), comment, srcPos);
+                            //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
+                            //     String8(comment).string());
+                        }
+                    } else if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
+                            break;
+                        }
+
+                        SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                "Found tag </%s> where </attr> is expected\n",
+                                String8(block.getElementName(&len)).string());
+                        return UNKNOWN_ERROR;
+                    }
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
+                err = compileAttribute(in, block, myPackage, outTable, NULL);
+                if (err != NO_ERROR) {
+                    hasErrors = true;
+                }
+                continue;
+
+            } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
+                curTag = &item16;
+                ssize_t attri = block.indexOfAttribute(NULL, "type");
+                if (attri >= 0) {
+                    curType = String16(block.getAttributeStringValue(attri, &len));
+                    ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
+                    if (formatIdx >= 0) {
+                        String16 formatStr = String16(block.getAttributeStringValue(
+                                formatIdx, &len));
+                        curFormat = parse_flags(formatStr.string(), formatStr.size(),
+                                                gFormatFlags);
+                        if (curFormat == 0) {
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                    "Tag <item> 'format' attribute value \"%s\" not valid\n",
+                                    String8(formatStr).string());
+                            hasErrors = localHasErrors = true;
+                        }
+                    }
+                } else {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                            "A 'type' attribute is required for <item>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                curIsStyled = true;
+            } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
+                // Note the existence and locale of every string we process
+                char rawLocale[16];
+                curParams.getLocale(rawLocale);
+                String8 locale(rawLocale);
+                String16 name;
+                String16 translatable;
+
+                size_t n = block.getAttributeCount();
+                for (size_t i = 0; i < n; i++) {
+                    size_t length;
+                    const uint16_t* attr = block.getAttributeName(i, &length);
+                    if (strcmp16(attr, name16.string()) == 0) {
+                        name.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, translatable16.string()) == 0) {
+                        translatable.setTo(block.getAttributeStringValue(i, &length));
+                    }
+                }
+                
+                if (name.size() > 0) {
+                    if (translatable == false16) {
+                        // Untranslatable strings must only exist in the default [empty] locale
+                        if (locale.size() > 0) {
+                            fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
+                                    " in locale '%s'\n", String8(name).string(),
+                                    bundle->getResourceSourceDirs()[0],
+                                    locale.string());
+                            // hasErrors = localHasErrors = true;
+                        } else {
+                            // Intentionally empty block:
+                            //
+                            // Don't add untranslatable strings to the localization table; that
+                            // way if we later see localizations of them, they'll be flagged as
+                            // having no default translation.
+                        }
+                    } else {
+                        outTable->addLocalization(name, locale);
+                    }
+                }
+
+                curTag = &string16;
+                curType = string16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
+                curIsStyled = true;
+                curIsPseudolocalizable = true;
+            } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
+                curTag = &drawable16;
+                curType = drawable16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
+            } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
+                curTag = &color16;
+                curType = color16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
+            } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
+                curTag = &bool16;
+                curType = bool16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
+            } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
+                curTag = &integer16;
+                curType = integer16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
+            } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
+                curTag = &dimen16;
+                curType = dimen16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
+            } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
+                curTag = &fraction16;
+                curType = fraction16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
+            } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
+                curTag = &bag16;
+                curIsBag = true;
+                ssize_t attri = block.indexOfAttribute(NULL, "type");
+                if (attri >= 0) {
+                    curType = String16(block.getAttributeStringValue(attri, &len));
+                } else {
+                    SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                            "A 'type' attribute is required for <bag>\n");
+                    hasErrors = localHasErrors = true;
+                }
+            } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
+                curTag = &style16;
+                curType = style16;
+                curIsBag = true;
+            } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
+                curTag = &plurals16;
+                curType = plurals16;
+                curIsBag = true;
+            } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
+                curTag = &array16;
+                curType = array16;
+                curIsBag = true;
+                ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
+                if (formatIdx >= 0) {
+                    String16 formatStr = String16(block.getAttributeStringValue(
+                            formatIdx, &len));
+                    curFormat = parse_flags(formatStr.string(), formatStr.size(),
+                                            gFormatFlags);
+                    if (curFormat == 0) {
+                        SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                "Tag <array> 'format' attribute value \"%s\" not valid\n",
+                                String8(formatStr).string());
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+            } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
+                curTag = &string_array16;
+                curType = array16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
+                curIsBag = true;
+                curIsPseudolocalizable = true;
+            } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
+                curTag = &integer_array16;
+                curType = array16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
+                curIsBag = true;
+            } else {
+                SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                        "Found tag %s where item is expected\n",
+                        String8(block.getElementName(&len)).string());
+                return UNKNOWN_ERROR;
+            }
+
+            String16 ident;
+            ssize_t identIdx = block.indexOfAttribute(NULL, "name");
+            if (identIdx >= 0) {
+                ident = String16(block.getAttributeStringValue(identIdx, &len));
+            } else {
+                SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                        "A 'name' attribute is required for <%s>\n",
+                        String8(*curTag).string());
+                hasErrors = localHasErrors = true;
+            }
+
+            String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
+            
+            if (curIsBag) {
+                // Figure out the parent of this bag...
+                String16 parentIdent;
+                ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
+                if (parentIdentIdx >= 0) {
+                    parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
+                } else {
+                    ssize_t sep = ident.findLast('.');
+                    if (sep >= 0) {
+                        parentIdent.setTo(ident, sep);
+                    }
+                }
+
+                if (!localHasErrors) {
+                    err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
+                                             myPackage, curType, ident, parentIdent, &curParams);
+                    if (err != NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+                
+                ssize_t elmIndex = 0;
+                char elmIndexStr[14];
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                        && code != ResXMLTree::BAD_DOCUMENT) {
+
+                    if (code == ResXMLTree::START_TAG) {
+                        if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                    "Tag <%s> can not appear inside <%s>, only <item>\n",
+                                    String8(block.getElementName(&len)).string(),
+                                    String8(*curTag).string());
+                            return UNKNOWN_ERROR;
+                        }
+
+                        String16 itemIdent;
+                        if (curType == array16) {
+                            sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
+                            itemIdent = String16(elmIndexStr);
+                        } else if (curType == plurals16) {
+                            ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
+                            if (itemIdentIdx >= 0) {
+                                String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
+                                if (quantity16 == other16) {
+                                    itemIdent = quantityOther16;
+                                }
+                                else if (quantity16 == zero16) {
+                                    itemIdent = quantityZero16;
+                                }
+                                else if (quantity16 == one16) {
+                                    itemIdent = quantityOne16;
+                                }
+                                else if (quantity16 == two16) {
+                                    itemIdent = quantityTwo16;
+                                }
+                                else if (quantity16 == few16) {
+                                    itemIdent = quantityFew16;
+                                }
+                                else if (quantity16 == many16) {
+                                    itemIdent = quantityMany16;
+                                }
+                                else {
+                                    SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                            "Illegal 'quantity' attribute is <item> inside <plurals>\n");
+                                    hasErrors = localHasErrors = true;
+                                }
+                            } else {
+                                SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                        "A 'quantity' attribute is required for <item> inside <plurals>\n");
+                                hasErrors = localHasErrors = true;
+                            }
+                        } else {
+                            ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
+                            if (itemIdentIdx >= 0) {
+                                itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
+                            } else {
+                                SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                        "A 'name' attribute is required for <item>\n");
+                                hasErrors = localHasErrors = true;
+                            }
+                        }
+
+                        ResXMLParser::ResXMLPosition parserPosition;
+                        block.getPosition(&parserPosition);
+
+                        err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
+                                ident, parentIdent, itemIdent, curFormat, 
+                                false, overwrite, outTable);
+                        if (err == NO_ERROR) {
+                            if (curIsPseudolocalizable && localeIsDefined(curParams)
+                                    && bundle->getPseudolocalize()) {
+                                // pseudolocalize here
+#if 1
+                                block.setPosition(parserPosition);
+                                err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
+                                        curType, ident, parentIdent, itemIdent, curFormat, true,
+                                        overwrite, outTable);
+#endif
+                            }
+                        } 
+                        if (err != NO_ERROR) {
+                            hasErrors = localHasErrors = true;
+                        }
+                    } else if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                                    "Found tag </%s> where </%s> is expected\n",
+                                    String8(block.getElementName(&len)).string(),
+                                    String8(*curTag).string());
+                            return UNKNOWN_ERROR;
+                        }
+                        break;
+                    }
+                }
+            } else {
+                ResXMLParser::ResXMLPosition parserPosition;
+                block.getPosition(&parserPosition);
+
+                err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
+                        *curTag, curIsStyled, curFormat, false, overwrite, outTable);
+
+                if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
+                    hasErrors = localHasErrors = true;
+                }
+                else if (err == NO_ERROR) {
+                    if (curIsPseudolocalizable && localeIsDefined(curParams)
+                            && bundle->getPseudolocalize()) {
+                        // pseudolocalize here
+                        block.setPosition(parserPosition);
+                        err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
+                                ident, *curTag, curIsStyled, curFormat, true, false, outTable);
+                        if (err != NO_ERROR) {
+                            hasErrors = localHasErrors = true;
+                        }
+                    }
+                }
+            }
+
+#if 0
+            if (comment.size() > 0) {
+                printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
+                       String8(curType).string(), String8(ident).string(),
+                       String8(comment).string());
+            }
+#endif
+            if (!localHasErrors) {
+                outTable->appendComment(myPackage, curType, ident, comment, false);
+            }
+        }
+        else if (code == ResXMLTree::END_TAG) {
+            if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
+                SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                        "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
+                return UNKNOWN_ERROR;
+            }
+        }
+        else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
+        }
+        else if (code == ResXMLTree::TEXT) {
+            if (isWhitespace(block.getText(&len))) {
+                continue;
+            }
+            SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
+                    "Found text \"%s\" where item tag is expected\n",
+                    String8(block.getText(&len)).string());
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
+    : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
+      mIsAppPackage(!bundle->getExtending()),
+      mNumLocal(0),
+      mBundle(bundle)
+{
+}
+
+status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+    status_t err = assets->buildIncludedResources(bundle);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // For future reference to included resources.
+    mAssets = assets;
+
+    const ResTable& incl = assets->getIncludedResources();
+
+    // Retrieve all the packages.
+    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));
+            uint32_t id = incl.getBasePackageId(i);
+            // First time through: only add base packages (id
+            // is not 0); second time through add the other
+            // packages.
+            if (phase != 0) {
+                if (id != 0) {
+                    // Skip base packages -- already one.
+                    id = 0;
+                } else {
+                    // Assign a dynamic id.
+                    id = mNextPackageId;
+                }
+            } else if (id != 0) {
+                if (id == 127) {
+                    if (mHaveAppPackage) {
+                        fprintf(stderr, "Included resource have two application packages!\n");
+                        return UNKNOWN_ERROR;
+                    }
+                    mHaveAppPackage = true;
+                }
+                if (mNextPackageId > id) {
+                    fprintf(stderr, "Included base package ID %d already in use!\n", id);
+                    return UNKNOWN_ERROR;
+                }
+            }
+            if (id != 0) {
+                NOISY(printf("Including package %s with ID=%d\n",
+                             String8(name).string(), id));
+                sp<Package> p = new Package(name, id);
+                mPackages.add(name, p);
+                mOrderedPackages.add(p);
+
+                if (id >= mNextPackageId) {
+                    mNextPackageId = id+1;
+                }
+            }
+        }
+    }
+
+    // Every resource table always has one first entry, the bag attributes.
+    const SourcePos unknown(String8("????"), 0);
+    sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
+
+    return NO_ERROR;
+}
+
+status_t ResourceTable::addPublic(const SourcePos& sourcePos,
+                                  const String16& package,
+                                  const String16& type,
+                                  const String16& name,
+                                  const uint32_t ident)
+{
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
+                String8(type).string(), String8(name).string(),
+                String8(package).string());
+        return UNKNOWN_ERROR;
+    }
+
+    sp<Type> t = getType(package, type, sourcePos);
+    if (t == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    return t->addPublic(sourcePos, name, ident);
+}
+
+status_t ResourceTable::addEntry(const SourcePos& sourcePos,
+                                 const String16& package,
+                                 const String16& type,
+                                 const String16& name,
+                                 const String16& value,
+                                 const Vector<StringPool::entry_style_span>* style,
+                                 const ResTable_config* params,
+                                 const bool doSetIndex,
+                                 const int32_t format,
+                                 const bool overwrite)
+{
+    // Check for adding entries in other packages...  for now we do
+    // nothing.  We need to do the right thing here to support skinning.
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        return NO_ERROR;
+    }
+    
+#if 0
+    if (name == String16("left")) {
+        printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
+               sourcePos.file.string(), sourcePos.line, String8(type).string(),
+               String8(value).string());
+    }
+#endif
+    
+    sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex);
+    if (e == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    status_t err = e->setItem(sourcePos, value, style, format, overwrite);
+    if (err == NO_ERROR) {
+        mNumLocal++;
+    }
+    return err;
+}
+
+status_t ResourceTable::startBag(const SourcePos& sourcePos,
+                                 const String16& package,
+                                 const String16& type,
+                                 const String16& name,
+                                 const String16& bagParent,
+                                 const ResTable_config* params,
+                                 bool replace, bool isId)
+{
+    // Check for adding entries in other packages...  for now we do
+    // nothing.  We need to do the right thing here to support skinning.
+    uint32_t rid = mAssets->getIncludedResources()
+    .identifierForName(name.string(), name.size(),
+                       type.string(), type.size(),
+                       package.string(), package.size());
+    if (rid != 0) {
+        return NO_ERROR;
+    }
+    
+#if 0
+    if (name == String16("left")) {
+        printf("Adding bag left: file=%s, line=%d, type=%s\n",
+               sourcePos.file.striing(), sourcePos.line, String8(type).string());
+    }
+#endif
+    
+    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    if (e == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    
+    // If a parent is explicitly specified, set it.
+    if (bagParent.size() > 0) {
+        String16 curPar = e->getParent();
+        if (curPar.size() > 0 && curPar != bagParent) {
+            sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
+                            String8(e->getParent()).string(),
+                            String8(bagParent).string());
+            return UNKNOWN_ERROR;
+        }
+        e->setParent(bagParent);
+    }
+    
+    return e->makeItABag(sourcePos);
+}
+
+status_t ResourceTable::addBag(const SourcePos& sourcePos,
+                               const String16& package,
+                               const String16& type,
+                               const String16& name,
+                               const String16& bagParent,
+                               const String16& bagKey,
+                               const String16& value,
+                               const Vector<StringPool::entry_style_span>* style,
+                               const ResTable_config* params,
+                               bool replace, bool isId, const int32_t format)
+{
+    // Check for adding entries in other packages...  for now we do
+    // nothing.  We need to do the right thing here to support skinning.
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        return NO_ERROR;
+    }
+
+#if 0
+    if (name == String16("left")) {
+        printf("Adding bag left: file=%s, line=%d, type=%s\n",
+               sourcePos.file.striing(), sourcePos.line, String8(type).string());
+    }
+#endif
+    
+    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    if (e == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    // If a parent is explicitly specified, set it.
+    if (bagParent.size() > 0) {
+        String16 curPar = e->getParent();
+        if (curPar.size() > 0 && curPar != bagParent) {
+            sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
+                    String8(e->getParent()).string(),
+                    String8(bagParent).string());
+            return UNKNOWN_ERROR;
+        }
+        e->setParent(bagParent);
+    }
+
+    const bool first = e->getBag().indexOfKey(bagKey) < 0;
+    status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
+    if (err == NO_ERROR && first) {
+        mNumLocal++;
+    }
+    return err;
+}
+
+bool ResourceTable::hasBagOrEntry(const String16& package,
+                                  const String16& type,
+                                  const String16& name) const
+{
+    // First look for this in the included resources...
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        return true;
+    }
+
+    sp<Package> p = mPackages.valueFor(package);
+    if (p != NULL) {
+        sp<Type> t = p->getTypes().valueFor(type);
+        if (t != NULL) {
+            sp<ConfigList> c =  t->getConfigs().valueFor(name);
+            if (c != NULL) return true;
+        }
+    }
+
+    return false;
+}
+
+bool ResourceTable::hasBagOrEntry(const String16& ref,
+                                  const String16* defType,
+                                  const String16* defPackage)
+{
+    String16 package, type, name;
+    if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
+                defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
+        return false;
+    }
+    return hasBagOrEntry(package, type, name);
+}
+
+bool ResourceTable::appendComment(const String16& package,
+                                  const String16& type,
+                                  const String16& name,
+                                  const String16& comment,
+                                  bool onlyIfEmpty)
+{
+    if (comment.size() <= 0) {
+        return true;
+    }
+
+    sp<Package> p = mPackages.valueFor(package);
+    if (p != NULL) {
+        sp<Type> t = p->getTypes().valueFor(type);
+        if (t != NULL) {
+            sp<ConfigList> c =  t->getConfigs().valueFor(name);
+            if (c != NULL) {
+                c->appendComment(comment, onlyIfEmpty);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool ResourceTable::appendTypeComment(const String16& package,
+                                      const String16& type,
+                                      const String16& name,
+                                      const String16& comment)
+{
+    if (comment.size() <= 0) {
+        return true;
+    }
+    
+    sp<Package> p = mPackages.valueFor(package);
+    if (p != NULL) {
+        sp<Type> t = p->getTypes().valueFor(type);
+        if (t != NULL) {
+            sp<ConfigList> c =  t->getConfigs().valueFor(name);
+            if (c != NULL) {
+                c->appendTypeComment(comment);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+size_t ResourceTable::size() const {
+    return mPackages.size();
+}
+
+size_t ResourceTable::numLocalResources() const {
+    return mNumLocal;
+}
+
+bool ResourceTable::hasResources() const {
+    return mNumLocal > 0;
+}
+
+sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
+{
+    sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
+    status_t err = flatten(bundle, data);
+    return err == NO_ERROR ? data : NULL;
+}
+
+inline uint32_t ResourceTable::getResId(const sp<Package>& p,
+                                        const sp<Type>& t,
+                                        uint32_t nameId)
+{
+    return makeResId(p->getAssignedId(), t->getIndex(), nameId);
+}
+
+uint32_t ResourceTable::getResId(const String16& package,
+                                 const String16& type,
+                                 const String16& name,
+                                 bool onlyPublic) const
+{
+    sp<Package> p = mPackages.valueFor(package);
+    if (p == NULL) return 0;
+
+    // First look for this in the included resources...
+    uint32_t specFlags = 0;
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size(),
+                           &specFlags);
+    if (rid != 0) {
+        if (onlyPublic) {
+            if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
+                return 0;
+            }
+        }
+        
+        if (Res_INTERNALID(rid)) {
+            return rid;
+        }
+        return Res_MAKEID(p->getAssignedId()-1,
+                          Res_GETTYPE(rid),
+                          Res_GETENTRY(rid));
+    }
+
+    sp<Type> t = p->getTypes().valueFor(type);
+    if (t == NULL) return 0;
+    sp<ConfigList> c =  t->getConfigs().valueFor(name);
+    if (c == NULL) return 0;
+    int32_t ei = c->getEntryIndex();
+    if (ei < 0) return 0;
+    return getResId(p, t, ei);
+}
+
+uint32_t ResourceTable::getResId(const String16& ref,
+                                 const String16* defType,
+                                 const String16* defPackage,
+                                 const char** outErrorMsg,
+                                 bool onlyPublic) const
+{
+    String16 package, type, name;
+    if (!ResTable::expandResourceRef(
+        ref.string(), ref.size(), &package, &type, &name,
+        defType, defPackage ? defPackage:&mAssetsPackage,
+        outErrorMsg)) {
+        NOISY(printf("Expanding resource: ref=%s\n",
+                     String8(ref).string()));
+        NOISY(printf("Expanding resource: defType=%s\n",
+                     defType ? String8(*defType).string() : "NULL"));
+        NOISY(printf("Expanding resource: defPackage=%s\n",
+                     defPackage ? String8(*defPackage).string() : "NULL"));
+        NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
+        NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
+                     String8(package).string(), String8(type).string(),
+                     String8(name).string()));
+        return 0;
+    }
+    uint32_t res = getResId(package, type, name, onlyPublic);
+    NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
+                 String8(package).string(), String8(type).string(),
+                 String8(name).string(), res));
+    if (res == 0) {
+        if (outErrorMsg)
+            *outErrorMsg = "No resource found that matches the given name";
+    }
+    return res;
+}
+
+bool ResourceTable::isValidResourceName(const String16& s)
+{
+    const char16_t* p = s.string();
+    bool first = true;
+    while (*p) {
+        if ((*p >= 'a' && *p <= 'z')
+            || (*p >= 'A' && *p <= 'Z')
+            || *p == '_'
+            || (!first && *p >= '0' && *p <= '9')) {
+            first = false;
+            p++;
+            continue;
+        }
+        return false;
+    }
+    return true;
+}
+
+bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
+                                  const String16& str,
+                                  bool preserveSpaces, bool coerceType,
+                                  uint32_t attrID,
+                                  const Vector<StringPool::entry_style_span>* style,
+                                  String16* outStr, void* accessorCookie,
+                                  uint32_t attrType)
+{
+    String16 finalStr;
+
+    bool res = true;
+    if (style == NULL || style->size() == 0) {
+        // Text is not styled so it can be any type...  let's figure it out.
+        res = mAssets->getIncludedResources()
+            .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
+                            coerceType, attrID, NULL, &mAssetsPackage, this,
+                           accessorCookie, attrType);
+    } else {
+        // Styled text can only be a string, and while collecting the style
+        // information we have already processed that string!
+        outValue->size = sizeof(Res_value);
+        outValue->res0 = 0;
+        outValue->dataType = outValue->TYPE_STRING;
+        outValue->data = 0;
+        finalStr = str;
+    }
+
+    if (!res) {
+        return false;
+    }
+
+    if (outValue->dataType == outValue->TYPE_STRING) {
+        // Should do better merging styles.
+        if (pool) {
+            if (style != NULL && style->size() > 0) {
+                outValue->data = pool->add(finalStr, *style);
+            } else {
+                outValue->data = pool->add(finalStr, true);
+            }
+        } else {
+            // Caller will fill this in later.
+            outValue->data = 0;
+        }
+    
+        if (outStr) {
+            *outStr = finalStr;
+        }
+
+    }
+
+    return true;
+}
+
+uint32_t ResourceTable::getCustomResource(
+    const String16& package, const String16& type, const String16& name) const
+{
+    //printf("getCustomResource: %s %s %s\n", String8(package).string(),
+    //       String8(type).string(), String8(name).string());
+    sp<Package> p = mPackages.valueFor(package);
+    if (p == NULL) return 0;
+    sp<Type> t = p->getTypes().valueFor(type);
+    if (t == NULL) return 0;
+    sp<ConfigList> c =  t->getConfigs().valueFor(name);
+    if (c == NULL) return 0;
+    int32_t ei = c->getEntryIndex();
+    if (ei < 0) return 0;
+    return getResId(p, t, ei);
+}
+
+uint32_t ResourceTable::getCustomResourceWithCreation(
+        const String16& package, const String16& type, const String16& name,
+        const bool createIfNotFound)
+{
+    uint32_t resId = getCustomResource(package, type, name);
+    if (resId != 0 || !createIfNotFound) {
+        return resId;
+    }
+    String16 value("false");
+
+    status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
+    if (status == NO_ERROR) {
+        resId = getResId(package, type, name);
+        return resId;
+    }
+    return 0;
+}
+
+uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
+{
+    return origPackage;
+}
+
+bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
+{
+    //printf("getAttributeType #%08x\n", attrID);
+    Res_value value;
+    if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
+        //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
+        //       String8(getEntry(attrID)->getName()).string(), value.data);
+        *outType = value.data;
+        return true;
+    }
+    return false;
+}
+
+bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
+{
+    //printf("getAttributeMin #%08x\n", attrID);
+    Res_value value;
+    if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
+        *outMin = value.data;
+        return true;
+    }
+    return false;
+}
+
+bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
+{
+    //printf("getAttributeMax #%08x\n", attrID);
+    Res_value value;
+    if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
+        *outMax = value.data;
+        return true;
+    }
+    return false;
+}
+
+uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
+{
+    //printf("getAttributeL10N #%08x\n", attrID);
+    Res_value value;
+    if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
+        return value.data;
+    }
+    return ResTable_map::L10N_NOT_REQUIRED;
+}
+
+bool ResourceTable::getLocalizationSetting()
+{
+    return mBundle->getRequireLocalization();
+}
+
+void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
+{
+    if (accessorCookie != NULL && fmt != NULL) {
+        AccessorCookie* ac = (AccessorCookie*)accessorCookie;
+        int retval=0;
+        char buf[1024];
+        va_list ap;
+        va_start(ap, fmt);
+        retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+        va_end(ap);
+        ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
+                            buf, ac->attr.string(), ac->value.string());
+    }
+}
+
+bool ResourceTable::getAttributeKeys(
+    uint32_t attrID, Vector<String16>* outKeys)
+{
+    sp<const Entry> e = getEntry(attrID);
+    if (e != NULL) {
+        const size_t N = e->getBag().size();
+        for (size_t i=0; i<N; i++) {
+            const String16& key = e->getBag().keyAt(i);
+            if (key.size() > 0 && key.string()[0] != '^') {
+                outKeys->add(key);
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool ResourceTable::getAttributeEnum(
+    uint32_t attrID, const char16_t* name, size_t nameLen,
+    Res_value* outValue)
+{
+    //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
+    String16 nameStr(name, nameLen);
+    sp<const Entry> e = getEntry(attrID);
+    if (e != NULL) {
+        const size_t N = e->getBag().size();
+        for (size_t i=0; i<N; i++) {
+            //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
+            //       String8(e->getBag().keyAt(i)).string());
+            if (e->getBag().keyAt(i) == nameStr) {
+                return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
+            }
+        }
+    }
+    return false;
+}
+
+bool ResourceTable::getAttributeFlags(
+    uint32_t attrID, const char16_t* name, size_t nameLen,
+    Res_value* outValue)
+{
+    outValue->dataType = Res_value::TYPE_INT_HEX;
+    outValue->data = 0;
+
+    //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
+    String16 nameStr(name, nameLen);
+    sp<const Entry> e = getEntry(attrID);
+    if (e != NULL) {
+        const size_t N = e->getBag().size();
+
+        const char16_t* end = name + nameLen;
+        const char16_t* pos = name;
+        bool failed = false;
+        while (pos < end && !failed) {
+            const char16_t* start = pos;
+            end++;
+            while (pos < end && *pos != '|') {
+                pos++;
+            }
+
+            String16 nameStr(start, pos-start);
+            size_t i;
+            for (i=0; i<N; i++) {
+                //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
+                //       String8(e->getBag().keyAt(i)).string());
+                if (e->getBag().keyAt(i) == nameStr) {
+                    Res_value val;
+                    bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
+                    if (!got) {
+                        return false;
+                    }
+                    //printf("Got value: 0x%08x\n", val.data);
+                    outValue->data |= val.data;
+                    break;
+                }
+            }
+
+            if (i >= N) {
+                // Didn't find this flag identifier.
+                return false;
+            }
+            if (pos < end) {
+                pos++;
+            }
+        }
+
+        return true;
+    }
+    return false;
+}
+
+status_t ResourceTable::assignResourceIds()
+{
+    const size_t N = mOrderedPackages.size();
+    size_t pi;
+    status_t firstError = NO_ERROR;
+
+    // First generate all bag attributes and assign indices.
+    for (pi=0; pi<N; pi++) {
+        sp<Package> p = mOrderedPackages.itemAt(pi);
+        if (p == NULL || p->getTypes().size() == 0) {
+            // Empty, skip!
+            continue;
+        }
+
+        status_t err = p->applyPublicTypeOrder();
+        if (err != NO_ERROR && firstError == NO_ERROR) {
+            firstError = err;
+        }
+
+        // Generate attributes...
+        const size_t N = p->getOrderedTypes().size();
+        size_t ti;
+        for (ti=0; ti<N; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                continue;
+            }
+            const size_t N = t->getOrderedConfigs().size();
+            for (size_t ci=0; ci<N; ci++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                if (c == NULL) {
+                    continue;
+                }
+                const size_t N = c->getEntries().size();
+                for (size_t ei=0; ei<N; ei++) {
+                    sp<Entry> e = c->getEntries().valueAt(ei);
+                    if (e == NULL) {
+                        continue;
+                    }
+                    status_t err = e->generateAttributes(this, p->getName());
+                    if (err != NO_ERROR && firstError == NO_ERROR) {
+                        firstError = err;
+                    }
+                }
+            }
+        }
+
+        const SourcePos unknown(String8("????"), 0);
+        sp<Type> attr = p->getType(String16("attr"), unknown);
+
+        // Assign indices...
+        for (ti=0; ti<N; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                continue;
+            }
+            err = t->applyPublicEntryOrder();
+            if (err != NO_ERROR && firstError == NO_ERROR) {
+                firstError = err;
+            }
+
+            const size_t N = t->getOrderedConfigs().size();
+            t->setIndex(ti+1);
+
+            LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
+                                "First type is not attr!");
+
+            for (size_t ei=0; ei<N; ei++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
+                if (c == NULL) {
+                    continue;
+                }
+                c->setEntryIndex(ei);
+            }
+        }
+
+        // Assign resource IDs to keys in bags...
+        for (ti=0; ti<N; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                continue;
+            }
+            const size_t N = t->getOrderedConfigs().size();
+            for (size_t ci=0; ci<N; ci++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                //printf("Ordered config #%d: %p\n", ci, c.get());
+                const size_t N = c->getEntries().size();
+                for (size_t ei=0; ei<N; ei++) {
+                    sp<Entry> e = c->getEntries().valueAt(ei);
+                    if (e == NULL) {
+                        continue;
+                    }
+                    status_t err = e->assignResourceIds(this, p->getName());
+                    if (err != NO_ERROR && firstError == NO_ERROR) {
+                        firstError = err;
+                    }
+                }
+            }
+        }
+    }
+    return firstError;
+}
+
+status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
+    const size_t N = mOrderedPackages.size();
+    size_t pi;
+
+    for (pi=0; pi<N; pi++) {
+        sp<Package> p = mOrderedPackages.itemAt(pi);
+        if (p->getTypes().size() == 0) {
+            // Empty, skip!
+            continue;
+        }
+
+        const size_t N = p->getOrderedTypes().size();
+        size_t ti;
+
+        for (ti=0; ti<N; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                continue;
+            }
+            const size_t N = t->getOrderedConfigs().size();
+            sp<AaptSymbols> typeSymbols;
+            typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
+            for (size_t ci=0; ci<N; ci++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                if (c == NULL) {
+                    continue;
+                }
+                uint32_t rid = getResId(p, t, ci);
+                if (rid == 0) {
+                    return UNKNOWN_ERROR;
+                }
+                if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
+                    typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
+                    
+                    String16 comment(c->getComment());
+                    typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
+                    //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
+                    //     String8(comment).string());
+                    comment = c->getTypeComment();
+                    typeSymbols->appendTypeComment(String8(c->getName()), comment);
+                } else {
+#if 0
+                    printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
+                           Res_GETPACKAGE(rid), p->getAssignedId());
+#endif
+                }
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+
+void
+ResourceTable::addLocalization(const String16& name, const String8& locale)
+{
+    mLocalizations[name].insert(locale);
+}
+
+
+/*!
+ * Flag various sorts of localization problems.  '+' indicates checks already implemented;
+ * '-' indicates checks that will be implemented in the future.
+ *
+ * + A localized string for which no default-locale version exists => warning
+ * + A string for which no version in an explicitly-requested locale exists => warning
+ * + A localized translation of an translateable="false" string => warning
+ * - A localized string not provided in every locale used by the table
+ */
+status_t
+ResourceTable::validateLocalizations(void)
+{
+    status_t err = NO_ERROR;
+    const String8 defaultLocale;
+
+    // For all strings...
+    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+         nameIter != mLocalizations.end();
+         nameIter++) {
+        const set<String8>& configSet = nameIter->second;   // naming convenience
+
+        // Look for strings with no default localization
+        if (configSet.count(defaultLocale) == 0) {
+            fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
+                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
+            for (set<String8>::iterator locales = configSet.begin();
+                 locales != configSet.end();
+                 locales++) {
+                fprintf(stdout, " %s", (*locales).string());
+            }
+            fprintf(stdout, "\n");
+            // !!! TODO: throw an error here in some circumstances
+        }
+
+        // Check that all requested localizations are present for this string
+        if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
+            const char* allConfigs = mBundle->getConfigurations();
+            const char* start = allConfigs;
+            const char* comma;
+            
+            do {
+                String8 config;
+                comma = strchr(start, ',');
+                if (comma != NULL) {
+                    config.setTo(start, comma - start);
+                    start = comma + 1;
+                } else {
+                    config.setTo(start);
+                }
+
+                // don't bother with the pseudolocale "zz_ZZ"
+                if (config != "zz_ZZ") {
+                    if (configSet.find(config) == configSet.end()) {
+                        // okay, no specific localization found.  it's possible that we are
+                        // requiring a specific regional localization [e.g. de_DE] but there is an
+                        // available string in the generic language localization [e.g. de];
+                        // consider that string to have fulfilled the localization requirement.
+                        String8 region(config.string(), 2);
+                        if (configSet.find(region) == configSet.end()) {
+                            if (configSet.count(defaultLocale) == 0) {
+                                fprintf(stdout, "aapt: error: "
+                                        "*** string '%s' has no default or required localization "
+                                        "for '%s' in %s\n",
+                                        String8(nameIter->first).string(),
+                                        config.string(),
+                                        mBundle->getResourceSourceDirs()[0]);
+                                err = UNKNOWN_ERROR;
+                            }
+                        }
+                    }
+                }
+           } while (comma != NULL);
+        }
+    }
+
+    return err;
+}
+
+
+status_t
+ResourceFilter::parse(const char* arg)
+{
+    if (arg == NULL) {
+        return 0;
+    }
+
+    const char* p = arg;
+    const char* q;
+
+    while (true) {
+        q = strchr(p, ',');
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+
+        String8 part(p, q-p);
+
+        if (part == "zz_ZZ") {
+            mContainsPseudo = true;
+        }
+        int axis;
+        uint32_t value;
+        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+            fprintf(stderr, "Invalid configuration: %s\n", arg);
+            fprintf(stderr, "                       ");
+            for (int i=0; i<p-arg; i++) {
+                fprintf(stderr, " ");
+            }
+            for (int i=0; i<q-p; i++) {
+                fprintf(stderr, "^");
+            }
+            fprintf(stderr, "\n");
+            return 1;
+        }
+
+        ssize_t index = mData.indexOfKey(axis);
+        if (index < 0) {
+            mData.add(axis, SortedVector<uint32_t>());
+        }
+        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+        sv.add(value);
+        // if it's a locale with a region, also match an unmodified locale of the
+        // same language
+        if (axis == AXIS_LANGUAGE) {
+            if (value & 0xffff0000) {
+                sv.add(value & 0x0000ffff);
+            }
+        }
+        p = q;
+        if (!*p) break;
+        p++;
+    }
+
+    return NO_ERROR;
+}
+
+bool
+ResourceFilter::match(int axis, uint32_t value)
+{
+    if (value == 0) {
+        // they didn't specify anything so take everything
+        return true;
+    }
+    ssize_t index = mData.indexOfKey(axis);
+    if (index < 0) {
+        // we didn't request anything on this axis so take everything
+        return true;
+    }
+    const SortedVector<uint32_t>& sv = mData.valueAt(index);
+    return sv.indexOf(value) >= 0;
+}
+
+bool
+ResourceFilter::match(const ResTable_config& config)
+{
+    if (config.locale) {
+        uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
+                | (config.language[1] << 8) | (config.language[0]);
+        if (!match(AXIS_LANGUAGE, locale)) {
+            return false;
+        }
+    }
+    if (!match(AXIS_ORIENTATION, config.orientation)) {
+        return false;
+    }
+    if (!match(AXIS_DENSITY, config.density)) {
+        return false;
+    }
+    if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
+        return false;
+    }
+    if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
+        return false;
+    }
+    if (!match(AXIS_KEYBOARD, config.keyboard)) {
+        return false;
+    }
+    if (!match(AXIS_NAVIGATION, config.navigation)) {
+        return false;
+    }
+    if (!match(AXIS_SCREENSIZE, config.screenSize)) {
+        return false;
+    }
+    if (!match(AXIS_VERSION, config.version)) {
+        return false;
+    }
+    return true;
+}
+
+status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
+{
+    ResourceFilter filter;
+    status_t err = filter.parse(bundle->getConfigurations());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    const size_t N = mOrderedPackages.size();
+    size_t pi;
+
+    // Iterate through all data, collecting all values (strings,
+    // references, etc).
+    StringPool valueStrings;
+    for (pi=0; pi<N; pi++) {
+        sp<Package> p = mOrderedPackages.itemAt(pi);
+        if (p->getTypes().size() == 0) {
+            // Empty, skip!
+            continue;
+        }
+
+        StringPool typeStrings;
+        StringPool keyStrings;
+
+        const size_t N = p->getOrderedTypes().size();
+        for (size_t ti=0; ti<N; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                typeStrings.add(String16("<empty>"), false);
+                continue;
+            }
+            typeStrings.add(t->getName(), false);
+
+            const size_t N = t->getOrderedConfigs().size();
+            for (size_t ci=0; ci<N; ci++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                if (c == NULL) {
+                    continue;
+                }
+                const size_t N = c->getEntries().size();
+                for (size_t ei=0; ei<N; ei++) {
+                    ConfigDescription config = c->getEntries().keyAt(ei);
+                    if (!filter.match(config)) {
+                        continue;
+                    }
+                    sp<Entry> e = c->getEntries().valueAt(ei);
+                    if (e == NULL) {
+                        continue;
+                    }
+                    e->setNameIndex(keyStrings.add(e->getName(), true));
+                    status_t err = e->prepareFlatten(&valueStrings, this);
+                    if (err != NO_ERROR) {
+                        return err;
+                    }
+                }
+            }
+        }
+
+        p->setTypeStrings(typeStrings.createStringBlock());
+        p->setKeyStrings(keyStrings.createStringBlock());
+    }
+
+    ssize_t strAmt = 0;
+    
+    // Now build the array of package chunks.
+    Vector<sp<AaptFile> > flatPackages;
+    for (pi=0; pi<N; pi++) {
+        sp<Package> p = mOrderedPackages.itemAt(pi);
+        if (p->getTypes().size() == 0) {
+            // Empty, skip!
+            continue;
+        }
+
+        const size_t N = p->getTypeStrings().size();
+
+        const size_t baseSize = sizeof(ResTable_package);
+
+        // Start the package data.
+        sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
+        ResTable_package* header = (ResTable_package*)data->editData(baseSize);
+        if (header == NULL) {
+            fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
+            return NO_MEMORY;
+        }
+        memset(header, 0, sizeof(*header));
+        header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
+        header->header.headerSize = htods(sizeof(*header));
+        header->id = htodl(p->getAssignedId());
+        strcpy16_htod(header->name, p->getName().string());
+
+        // Write the string blocks.
+        const size_t typeStringsStart = data->getSize();
+        sp<AaptFile> strFile = p->getTypeStringsData();
+        ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
+        #if PRINT_STRING_METRICS
+        fprintf(stderr, "**** type strings: %d\n", amt);
+        #endif
+        strAmt += amt;
+        if (amt < 0) {
+            return amt;
+        }
+        const size_t keyStringsStart = data->getSize();
+        strFile = p->getKeyStringsData();
+        amt = data->writeData(strFile->getData(), strFile->getSize());
+        #if PRINT_STRING_METRICS
+        fprintf(stderr, "**** key strings: %d\n", amt);
+        #endif
+        strAmt += amt;
+        if (amt < 0) {
+            return amt;
+        }
+
+        // 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.
+            size_t len;
+            String16 typeName(p->getTypeStrings().stringAt(ti, &len));
+            sp<Type> t = p->getTypes().valueFor(typeName);
+            LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
+                                "Type name %s not found",
+                                String8(typeName).string());
+
+            const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
+            
+            // First write the typeSpec chunk, containing information about
+            // each resource entry in this type.
+            {
+                const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
+                const size_t typeSpecStart = data->getSize();
+                ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
+                    (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
+                if (tsHeader == NULL) {
+                    fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
+                    return NO_MEMORY;
+                }
+                memset(tsHeader, 0, sizeof(*tsHeader));
+                tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
+                tsHeader->header.headerSize = htods(sizeof(*tsHeader));
+                tsHeader->header.size = htodl(typeSpecSize);
+                tsHeader->id = ti+1;
+                tsHeader->entryCount = htodl(N);
+                
+                uint32_t* typeSpecFlags = (uint32_t*)
+                    (((uint8_t*)data->editData())
+                        + typeSpecStart + sizeof(ResTable_typeSpec));
+                memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
+                        
+                for (size_t ei=0; ei<N; ei++) {
+                    sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
+                    if (cl->getPublic()) {
+                        typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
+                    }
+                    const size_t CN = cl->getEntries().size();
+                    for (size_t ci=0; ci<CN; ci++) {
+                        if (!filter.match(cl->getEntries().keyAt(ci))) {
+                            continue;
+                        }
+                        for (size_t cj=ci+1; cj<CN; cj++) {
+                            if (!filter.match(cl->getEntries().keyAt(cj))) {
+                                continue;
+                            }
+                            typeSpecFlags[ei] |= htodl(
+                                cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
+                        }
+                    }
+                }
+            }
+            
+            // We need to write one type chunk for each configuration for
+            // which we have entries in this type.
+            const size_t NC = t->getUniqueConfigs().size();
+            
+            const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
+            
+            for (size_t ci=0; ci<NC; ci++) {
+                ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
+
+                NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
+                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                      ti+1,
+                      config.mcc, config.mnc,
+                      config.language[0] ? config.language[0] : '-',
+                      config.language[1] ? config.language[1] : '-',
+                      config.country[0] ? config.country[0] : '-',
+                      config.country[1] ? config.country[1] : '-',
+                      config.orientation,
+                      config.touchscreen,
+                      config.density,
+                      config.keyboard,
+                      config.inputFlags,
+                      config.navigation,
+                      config.screenWidth,
+                      config.screenHeight));
+                      
+                if (!filter.match(config)) {
+                    continue;
+                }
+                
+                const size_t typeStart = data->getSize();
+
+                ResTable_type* tHeader = (ResTable_type*)
+                    (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
+                if (tHeader == NULL) {
+                    fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
+                    return NO_MEMORY;
+                }
+
+                memset(tHeader, 0, sizeof(*tHeader));
+                tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
+                tHeader->header.headerSize = htods(sizeof(*tHeader));
+                tHeader->id = ti+1;
+                tHeader->entryCount = htodl(N);
+                tHeader->entriesStart = htodl(typeSize);
+                tHeader->config = config;
+                NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
+                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                      ti+1,
+                      tHeader->config.mcc, tHeader->config.mnc,
+                      tHeader->config.language[0] ? tHeader->config.language[0] : '-',
+                      tHeader->config.language[1] ? tHeader->config.language[1] : '-',
+                      tHeader->config.country[0] ? tHeader->config.country[0] : '-',
+                      tHeader->config.country[1] ? tHeader->config.country[1] : '-',
+                      tHeader->config.orientation,
+                      tHeader->config.touchscreen,
+                      tHeader->config.density,
+                      tHeader->config.keyboard,
+                      tHeader->config.inputFlags,
+                      tHeader->config.navigation,
+                      tHeader->config.screenWidth,
+                      tHeader->config.screenHeight));
+                tHeader->config.swapHtoD();
+
+                // Build the entries inside of this type.
+                for (size_t ei=0; ei<N; ei++) {
+                    sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
+                    sp<Entry> e = cl->getEntries().valueFor(config);
+
+                    // Set the offset for this entry in its type.
+                    uint32_t* index = (uint32_t*)
+                        (((uint8_t*)data->editData())
+                            + typeStart + sizeof(ResTable_type));
+                    if (e != NULL) {
+                        index[ei] = htodl(data->getSize()-typeStart-typeSize);
+
+                        // Create the entry.
+                        ssize_t amt = e->flatten(bundle, data, cl->getPublic());
+                        if (amt < 0) {
+                            return amt;
+                        }
+                    } else {
+                        index[ei] = htodl(ResTable_type::NO_ENTRY);
+                    }
+                }
+
+                // Fill in the rest of the type information.
+                tHeader = (ResTable_type*)
+                    (((uint8_t*)data->editData()) + typeStart);
+                tHeader->header.size = htodl(data->getSize()-typeStart);
+            }
+        }
+
+        // Fill in the rest of the package information.
+        header = (ResTable_package*)data->editData();
+        header->header.size = htodl(data->getSize());
+        header->typeStrings = htodl(typeStringsStart);
+        header->lastPublicType = htodl(p->getTypeStrings().size());
+        header->keyStrings = htodl(keyStringsStart);
+        header->lastPublicKey = htodl(p->getKeyStrings().size());
+
+        flatPackages.add(data);
+    }
+
+    // And now write out the final chunks.
+    const size_t dataStart = dest->getSize();
+
+    {
+        // blah
+        ResTable_header header;
+        memset(&header, 0, sizeof(header));
+        header.header.type = htods(RES_TABLE_TYPE);
+        header.header.headerSize = htods(sizeof(header));
+        header.packageCount = htodl(flatPackages.size());
+        status_t err = dest->writeData(&header, sizeof(header));
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
+            return err;
+        }
+    }
+    
+    ssize_t strStart = dest->getSize();
+    err = valueStrings.writeStringBlock(dest);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    ssize_t amt = (dest->getSize()-strStart);
+    strAmt += amt;
+    #if PRINT_STRING_METRICS
+    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());
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
+            return err;
+        }
+    }
+
+    ResTable_header* header = (ResTable_header*)
+        (((uint8_t*)dest->getData()) + dataStart);
+    header->header.size = htodl(dest->getSize() - dataStart);
+
+    NOISY(aout << "Resource table:"
+          << HexDump(dest->getData(), dest->getSize()) << endl);
+
+    #if PRINT_STRING_METRICS
+    fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
+        dest->getSize(), (strAmt*100)/dest->getSize());
+    #endif
+    
+    return NO_ERROR;
+}
+
+void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
+{
+    fprintf(fp,
+    "<!-- This file contains <public> resource definitions for all\n"
+    "     resources that were generated from the source data. -->\n"
+    "\n"
+    "<resources>\n");
+
+    writePublicDefinitions(package, fp, true);
+    writePublicDefinitions(package, fp, false);
+
+    fprintf(fp,
+    "\n"
+    "</resources>\n");
+}
+
+void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
+{
+    bool didHeader = false;
+
+    sp<Package> pkg = mPackages.valueFor(package);
+    if (pkg != NULL) {
+        const size_t NT = pkg->getOrderedTypes().size();
+        for (size_t i=0; i<NT; i++) {
+            sp<Type> t = pkg->getOrderedTypes().itemAt(i);
+            if (t == NULL) {
+                continue;
+            }
+
+            bool didType = false;
+
+            const size_t NC = t->getOrderedConfigs().size();
+            for (size_t j=0; j<NC; j++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
+                if (c == NULL) {
+                    continue;
+                }
+
+                if (c->getPublic() != pub) {
+                    continue;
+                }
+
+                if (!didType) {
+                    fprintf(fp, "\n");
+                    didType = true;
+                }
+                if (!didHeader) {
+                    if (pub) {
+                        fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
+                        fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
+                    } else {
+                        fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
+                        fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
+                    }
+                    didHeader = true;
+                }
+                if (!pub) {
+                    const size_t NE = c->getEntries().size();
+                    for (size_t k=0; k<NE; k++) {
+                        const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
+                        if (pos.file != "") {
+                            fprintf(fp,"  <!-- Declared at %s:%d -->\n",
+                                    pos.file.string(), pos.line);
+                        }
+                    }
+                }
+                fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
+                        String8(t->getName()).string(),
+                        String8(c->getName()).string(),
+                        getResId(pkg, t, c->getEntryIndex()));
+            }
+        }
+    }
+}
+
+ResourceTable::Item::Item(const SourcePos& _sourcePos,
+                          bool _isId,
+                          const String16& _value,
+                          const Vector<StringPool::entry_style_span>* _style,
+                          int32_t _format)
+    : sourcePos(_sourcePos)
+    , isId(_isId)
+    , value(_value)
+    , format(_format)
+    , bagKeyId(0)
+    , evaluating(false)
+{
+    if (_style) {
+        style = *_style;
+    }
+}
+
+status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
+{
+    if (mType == TYPE_BAG) {
+        return NO_ERROR;
+    }
+    if (mType == TYPE_UNKNOWN) {
+        mType = TYPE_BAG;
+        return NO_ERROR;
+    }
+    sourcePos.error("Resource entry %s is already defined as a single item.\n"
+                    "%s:%d: Originally defined here.\n",
+                    String8(mName).string(),
+                    mItem.sourcePos.file.string(), mItem.sourcePos.line);
+    return UNKNOWN_ERROR;
+}
+
+status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
+                                       const String16& value,
+                                       const Vector<StringPool::entry_style_span>* style,
+                                       int32_t format,
+                                       const bool overwrite)
+{
+    Item item(sourcePos, false, value, style);
+
+    if (mType == TYPE_BAG) {
+        const Item& item(mBag.valueAt(0));
+        sourcePos.error("Resource entry %s is already defined as a bag.\n"
+                        "%s:%d: Originally defined here.\n",
+                        String8(mName).string(),
+                        item.sourcePos.file.string(), item.sourcePos.line);
+        return UNKNOWN_ERROR;
+    }
+    if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
+        sourcePos.error("Resource entry %s is already defined.\n"
+                        "%s:%d: Originally defined here.\n",
+                        String8(mName).string(),
+                        mItem.sourcePos.file.string(), mItem.sourcePos.line);
+        return UNKNOWN_ERROR;
+    }
+    
+    mType = TYPE_ITEM;
+    mItem = item;
+    mItemFormat = format;
+    return NO_ERROR;
+}
+
+status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
+                                        const String16& key, const String16& value,
+                                        const Vector<StringPool::entry_style_span>* style,
+                                        bool replace, bool isId, int32_t format)
+{
+    status_t err = makeItABag(sourcePos);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    Item item(sourcePos, isId, value, style, format);
+    
+    // XXX NOTE: there is an error if you try to have a bag with two keys,
+    // one an attr and one an id, with the same name.  Not something we
+    // currently ever have to worry about.
+    ssize_t origKey = mBag.indexOfKey(key);
+    if (origKey >= 0) {
+        if (!replace) {
+            const Item& item(mBag.valueAt(origKey));
+            sourcePos.error("Resource entry %s already has bag item %s.\n"
+                    "%s:%d: Originally defined here.\n",
+                    String8(mName).string(), String8(key).string(),
+                    item.sourcePos.file.string(), item.sourcePos.line);
+            return UNKNOWN_ERROR;
+        }
+        //printf("Replacing %s with %s\n",
+        //       String8(mBag.valueFor(key).value).string(), String8(value).string());
+        mBag.replaceValueFor(key, item);
+    }
+
+    mBag.add(key, item);
+    return NO_ERROR;
+}
+
+status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
+                                                  const String16& package)
+{
+    const String16 attr16("attr");
+    const String16 id16("id");
+    const size_t N = mBag.size();
+    for (size_t i=0; i<N; i++) {
+        const String16& key = mBag.keyAt(i);
+        const Item& it = mBag.valueAt(i);
+        if (it.isId) {
+            if (!table->hasBagOrEntry(key, &id16, &package)) {
+                String16 value("false");
+                status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
+                                               id16, key, value);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+            }
+        } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
+
+#if 1
+//             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
+//                     String8(key).string());
+//             const Item& item(mBag.valueAt(i));
+//             fprintf(stderr, "Referenced from file %s line %d\n",
+//                     item.sourcePos.file.string(), item.sourcePos.line);
+//             return UNKNOWN_ERROR;
+#else
+            char numberStr[16];
+            sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
+            status_t err = table->addBag(SourcePos("<generated>", 0), package,
+                                         attr16, key, String16(""),
+                                         String16("^type"),
+                                         String16(numberStr), NULL, NULL);
+            if (err != NO_ERROR) {
+                return err;
+            }
+#endif
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
+                                                 const String16& package)
+{
+    bool hasErrors = false;
+    
+    if (mType == TYPE_BAG) {
+        const char* errorMsg;
+        const String16 style16("style");
+        const String16 attr16("attr");
+        const String16 id16("id");
+        mParentId = 0;
+        if (mParent.size() > 0) {
+            mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
+            if (mParentId == 0) {
+                mPos.error("Error retrieving parent for item: %s '%s'.\n",
+                        errorMsg, String8(mParent).string());
+                hasErrors = true;
+            }
+        }
+        const size_t N = mBag.size();
+        for (size_t i=0; i<N; i++) {
+            const String16& key = mBag.keyAt(i);
+            Item& it = mBag.editValueAt(i);
+            it.bagKeyId = table->getResId(key,
+                    it.isId ? &id16 : &attr16, NULL, &errorMsg);
+            //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
+            if (it.bagKeyId == 0) {
+                it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
+                        String8(it.isId ? id16 : attr16).string(),
+                        String8(key).string());
+                hasErrors = true;
+            }
+        }
+    }
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
+{
+    if (mType == TYPE_ITEM) {
+        Item& it = mItem;
+        AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
+        if (!table->stringToValue(&it.parsedValue, strings,
+                                  it.value, false, true, 0,
+                                  &it.style, NULL, &ac, mItemFormat)) {
+            return UNKNOWN_ERROR;
+        }
+    } else if (mType == TYPE_BAG) {
+        const size_t N = mBag.size();
+        for (size_t i=0; i<N; i++) {
+            const String16& key = mBag.keyAt(i);
+            Item& it = mBag.editValueAt(i);
+            AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
+            if (!table->stringToValue(&it.parsedValue, strings,
+                                      it.value, false, true, it.bagKeyId,
+                                      &it.style, NULL, &ac, it.format)) {
+                return UNKNOWN_ERROR;
+            }
+        }
+    } else {
+        mPos.error("Error: entry %s is not a single item or a bag.\n",
+                   String8(mName).string());
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
+{
+    size_t amt = 0;
+    ResTable_entry header;
+    memset(&header, 0, sizeof(header));
+    header.size = htods(sizeof(header));
+    const type ty = this != NULL ? mType : TYPE_ITEM;
+    if (this != NULL) {
+        if (ty == TYPE_BAG) {
+            header.flags |= htods(header.FLAG_COMPLEX);
+        }
+        if (isPublic) {
+            header.flags |= htods(header.FLAG_PUBLIC);
+        }
+        header.key.index = htodl(mNameIndex);
+    }
+    if (ty != TYPE_BAG) {
+        status_t err = data->writeData(&header, sizeof(header));
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
+            return err;
+        }
+
+        const Item& it = mItem;
+        Res_value par;
+        memset(&par, 0, sizeof(par));
+        par.size = htods(it.parsedValue.size);
+        par.dataType = it.parsedValue.dataType;
+        par.res0 = it.parsedValue.res0;
+        par.data = htodl(it.parsedValue.data);
+        #if 0
+        printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
+               String8(mName).string(), it.parsedValue.dataType,
+               it.parsedValue.data, par.res0);
+        #endif
+        err = data->writeData(&par, it.parsedValue.size);
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: out of memory creating Res_value\n");
+            return err;
+        }
+        amt += it.parsedValue.size;
+    } else {
+        size_t N = mBag.size();
+        size_t i;
+        // Create correct ordering of items.
+        KeyedVector<uint32_t, const Item*> items;
+        for (i=0; i<N; i++) {
+            const Item& it = mBag.valueAt(i);
+            items.add(it.bagKeyId, &it);
+        }
+        N = items.size();
+        
+        ResTable_map_entry mapHeader;
+        memcpy(&mapHeader, &header, sizeof(header));
+        mapHeader.size = htods(sizeof(mapHeader));
+        mapHeader.parent.ident = htodl(mParentId);
+        mapHeader.count = htodl(N);
+        status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
+            return err;
+        }
+
+        for (i=0; i<N; i++) {
+            const Item& it = *items.valueAt(i);
+            ResTable_map map;
+            map.name.ident = htodl(it.bagKeyId);
+            map.value.size = htods(it.parsedValue.size);
+            map.value.dataType = it.parsedValue.dataType;
+            map.value.res0 = it.parsedValue.res0;
+            map.value.data = htodl(it.parsedValue.data);
+            err = data->writeData(&map, sizeof(map));
+            if (err != NO_ERROR) {
+                fprintf(stderr, "ERROR: out of memory creating Res_value\n");
+                return err;
+            }
+            amt += sizeof(map);
+        }
+    }
+    return amt;
+}
+
+void ResourceTable::ConfigList::appendComment(const String16& comment,
+                                              bool onlyIfEmpty)
+{
+    if (comment.size() <= 0) {
+        return;
+    }
+    if (onlyIfEmpty && mComment.size() > 0) {
+        return;
+    }
+    if (mComment.size() > 0) {
+        mComment.append(String16("\n"));
+    }
+    mComment.append(comment);
+}
+
+void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
+{
+    if (comment.size() <= 0) {
+        return;
+    }
+    if (mTypeComment.size() > 0) {
+        mTypeComment.append(String16("\n"));
+    }
+    mTypeComment.append(comment);
+}
+
+status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
+                                        const String16& name,
+                                        const uint32_t ident)
+{
+    #if 0
+    int32_t entryIdx = Res_GETENTRY(ident);
+    if (entryIdx < 0) {
+        sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
+                String8(mName).string(), String8(name).string(), ident);
+        return UNKNOWN_ERROR;
+    }
+    #endif
+
+    int32_t typeIdx = Res_GETTYPE(ident);
+    if (typeIdx >= 0) {
+        typeIdx++;
+        if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
+            sourcePos.error("Public resource %s/%s has conflicting type codes for its"
+                    " public identifiers (0x%x vs 0x%x).\n",
+                    String8(mName).string(), String8(name).string(),
+                    mPublicIndex, typeIdx);
+            return UNKNOWN_ERROR;
+        }
+        mPublicIndex = typeIdx;
+    }
+
+    if (mFirstPublicSourcePos == NULL) {
+        mFirstPublicSourcePos = new SourcePos(sourcePos);
+    }
+
+    if (mPublic.indexOfKey(name) < 0) {
+        mPublic.add(name, Public(sourcePos, String16(), ident));
+    } else {
+        Public& p = mPublic.editValueFor(name);
+        if (p.ident != ident) {
+            sourcePos.error("Public resource %s/%s has conflicting public identifiers"
+                    " (0x%08x vs 0x%08x).\n"
+                    "%s:%d: Originally defined here.\n",
+                    String8(mName).string(), String8(name).string(), p.ident, ident,
+                    p.sourcePos.file.string(), p.sourcePos.line);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
+                                                       const SourcePos& sourcePos,
+                                                       const ResTable_config* config,
+                                                       bool doSetIndex)
+{
+    int pos = -1;
+    sp<ConfigList> c = mConfigs.valueFor(entry);
+    if (c == NULL) {
+        c = new ConfigList(entry, sourcePos);
+        mConfigs.add(entry, c);
+        pos = (int)mOrderedConfigs.size();
+        mOrderedConfigs.add(c);
+        if (doSetIndex) {
+            c->setEntryIndex(pos);
+        }
+    }
+    
+    ConfigDescription cdesc;
+    if (config) cdesc = *config;
+    
+    sp<Entry> e = c->getEntries().valueFor(cdesc);
+    if (e == NULL) {
+        if (config != NULL) {
+            NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
+                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                      sourcePos.file.string(), sourcePos.line,
+                      config->mcc, config->mnc,
+                      config->language[0] ? config->language[0] : '-',
+                      config->language[1] ? config->language[1] : '-',
+                      config->country[0] ? config->country[0] : '-',
+                      config->country[1] ? config->country[1] : '-',
+                      config->orientation,
+                      config->touchscreen,
+                      config->density,
+                      config->keyboard,
+                      config->inputFlags,
+                      config->navigation,
+                      config->screenWidth,
+                      config->screenHeight));
+        } else {
+            NOISY(printf("New entry at %s:%d: NULL config\n",
+                      sourcePos.file.string(), sourcePos.line));
+        }
+        e = new Entry(entry, sourcePos);
+        c->addEntry(cdesc, e);
+        /*
+        if (doSetIndex) {
+            if (pos < 0) {
+                for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
+                    if (mOrderedConfigs[pos] == c) {
+                        break;
+                    }
+                }
+                if (pos >= (int)mOrderedConfigs.size()) {
+                    sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
+                    return NULL;
+                }
+            }
+            e->setEntryIndex(pos);
+        }
+        */
+    }
+    
+    mUniqueConfigs.add(cdesc);
+    
+    return e;
+}
+
+status_t ResourceTable::Type::applyPublicEntryOrder()
+{
+    size_t N = mOrderedConfigs.size();
+    Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
+    bool hasError = false;
+
+    size_t i;
+    for (i=0; i<N; i++) {
+        mOrderedConfigs.replaceAt(NULL, i);
+    }
+
+    const size_t NP = mPublic.size();
+    //printf("Ordering %d configs from %d public defs\n", N, NP);
+    size_t j;
+    for (j=0; j<NP; j++) {
+        const String16& name = mPublic.keyAt(j);
+        const Public& p = mPublic.valueAt(j);
+        int32_t idx = Res_GETENTRY(p.ident);
+        //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
+        //       String8(mName).string(), String8(name).string(), p.ident, N);
+        bool found = false;
+        for (i=0; i<N; i++) {
+            sp<ConfigList> e = origOrder.itemAt(i);
+            //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
+            if (e->getName() == name) {
+                if (idx >= (int32_t)mOrderedConfigs.size()) {
+                    p.sourcePos.error("Public entry identifier 0x%x entry index "
+                            "is larger than available symbols (index %d, total symbols %d).\n",
+                            p.ident, idx, mOrderedConfigs.size());
+                    hasError = true;
+                } else if (mOrderedConfigs.itemAt(idx) == NULL) {
+                    e->setPublic(true);
+                    e->setPublicSourcePos(p.sourcePos);
+                    mOrderedConfigs.replaceAt(e, idx);
+                    origOrder.removeAt(i);
+                    N--;
+                    found = true;
+                    break;
+                } else {
+                    sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
+
+                    p.sourcePos.error("Multiple entry names declared for public entry"
+                            " identifier 0x%x in type %s (%s vs %s).\n"
+                            "%s:%d: Originally defined here.",
+                            idx+1, String8(mName).string(),
+                            String8(oe->getName()).string(),
+                            String8(name).string(),
+                            oe->getPublicSourcePos().file.string(),
+                            oe->getPublicSourcePos().line);
+                    hasError = true;
+                }
+            }
+        }
+
+        if (!found) {
+            p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
+                    String8(mName).string(), String8(name).string());
+            hasError = true;
+        }
+    }
+
+    //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
+    
+    if (N != origOrder.size()) {
+        printf("Internal error: remaining private symbol count mismatch\n");
+        N = origOrder.size();
+    }
+    
+    j = 0;
+    for (i=0; i<N; i++) {
+        sp<ConfigList> e = origOrder.itemAt(i);
+        // There will always be enough room for the remaining entries.
+        while (mOrderedConfigs.itemAt(j) != NULL) {
+            j++;
+        }
+        mOrderedConfigs.replaceAt(e, j);
+        j++;
+    }
+
+    return hasError ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+ResourceTable::Package::Package(const String16& name, ssize_t includedId)
+    : mName(name), mIncludedId(includedId),
+      mTypeStringsMapping(0xffffffff),
+      mKeyStringsMapping(0xffffffff)
+{
+}
+
+sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
+                                                        const SourcePos& sourcePos,
+                                                        bool doSetIndex)
+{
+    sp<Type> t = mTypes.valueFor(type);
+    if (t == NULL) {
+        t = new Type(type, sourcePos);
+        mTypes.add(type, t);
+        mOrderedTypes.add(t);
+        if (doSetIndex) {
+            // For some reason the type's index is set to one plus the index
+            // in the mOrderedTypes list, rather than just the index.
+            t->setIndex(mOrderedTypes.size());
+        }
+    }
+    return t;
+}
+
+status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
+{
+    mTypeStringsData = data;
+    status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "ERROR: Type string data is corrupt!\n");
+    }
+    return err;
+}
+
+status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
+{
+    mKeyStringsData = data;
+    status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "ERROR: Key string data is corrupt!\n");
+    }
+    return err;
+}
+
+status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
+                                            ResStringPool* strings,
+                                            DefaultKeyedVector<String16, uint32_t>* mappings)
+{
+    if (data->getData() == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    NOISY(aout << "Setting restable string pool: "
+          << HexDump(data->getData(), data->getSize()) << endl);
+
+    status_t err = strings->setTo(data->getData(), data->getSize());
+    if (err == NO_ERROR) {
+        const size_t N = strings->size();
+        for (size_t i=0; i<N; i++) {
+            size_t len;
+            mappings->add(String16(strings->stringAt(i, &len)), i);
+        }
+    }
+    return err;
+}
+
+status_t ResourceTable::Package::applyPublicTypeOrder()
+{
+    size_t N = mOrderedTypes.size();
+    Vector<sp<Type> > origOrder(mOrderedTypes);
+
+    size_t i;
+    for (i=0; i<N; i++) {
+        mOrderedTypes.replaceAt(NULL, i);
+    }
+
+    for (i=0; i<N; i++) {
+        sp<Type> t = origOrder.itemAt(i);
+        int32_t idx = t->getPublicIndex();
+        if (idx > 0) {
+            idx--;
+            while (idx >= (int32_t)mOrderedTypes.size()) {
+                mOrderedTypes.add();
+            }
+            if (mOrderedTypes.itemAt(idx) != NULL) {
+                sp<Type> ot = mOrderedTypes.itemAt(idx);
+                t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
+                        " identifier 0x%x (%s vs %s).\n"
+                        "%s:%d: Originally defined here.",
+                        idx, String8(ot->getName()).string(),
+                        String8(t->getName()).string(),
+                        ot->getFirstPublicSourcePos().file.string(),
+                        ot->getFirstPublicSourcePos().line);
+                return UNKNOWN_ERROR;
+            }
+            mOrderedTypes.replaceAt(t, idx);
+            origOrder.removeAt(i);
+            i--;
+            N--;
+        }
+    }
+
+    size_t j=0;
+    for (i=0; i<N; i++) {
+        sp<Type> t = origOrder.itemAt(i);
+        // There will always be enough room for the remaining types.
+        while (mOrderedTypes.itemAt(j) != NULL) {
+            j++;
+        }
+        mOrderedTypes.replaceAt(t, j);
+    }
+
+    return NO_ERROR;
+}
+
+sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
+{
+    sp<Package> p = mPackages.valueFor(package);
+    if (p == NULL) {
+        if (mIsAppPackage) {
+            if (mHaveAppPackage) {
+                fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
+                                "Use -x to create extended resources.\n");
+                return NULL;
+            }
+            mHaveAppPackage = true;
+            p = new Package(package, 127);
+        } else {
+            p = new Package(package, mNextPackageId);
+        }
+        //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
+        //       String8(package).string(), p->getAssignedId());
+        mPackages.add(package, p);
+        mOrderedPackages.add(p);
+        mNextPackageId++;
+    }
+    return p;
+}
+
+sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
+                                               const String16& type,
+                                               const SourcePos& sourcePos,
+                                               bool doSetIndex)
+{
+    sp<Package> p = getPackage(package);
+    if (p == NULL) {
+        return NULL;
+    }
+    return p->getType(type, sourcePos, doSetIndex);
+}
+
+sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
+                                                 const String16& type,
+                                                 const String16& name,
+                                                 const SourcePos& sourcePos,
+                                                 const ResTable_config* config,
+                                                 bool doSetIndex)
+{
+    sp<Type> t = getType(package, type, sourcePos, doSetIndex);
+    if (t == NULL) {
+        return NULL;
+    }
+    return t->getEntry(name, sourcePos, config, doSetIndex);
+}
+
+sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
+                                                       const ResTable_config* config) const
+{
+    int pid = Res_GETPACKAGE(resID)+1;
+    const size_t N = mOrderedPackages.size();
+    size_t i;
+    sp<Package> p;
+    for (i=0; i<N; i++) {
+        sp<Package> check = mOrderedPackages[i];
+        if (check->getAssignedId() == pid) {
+            p = check;
+            break;
+        }
+
+    }
+    if (p == NULL) {
+        fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
+        return NULL;
+    }
+
+    int tid = Res_GETTYPE(resID);
+    if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
+        fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
+        return NULL;
+    }
+    sp<Type> t = p->getOrderedTypes()[tid];
+
+    int eid = Res_GETENTRY(resID);
+    if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
+        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        return NULL;
+    }
+
+    sp<ConfigList> c = t->getOrderedConfigs()[eid];
+    if (c == NULL) {
+        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        return NULL;
+    }
+    
+    ConfigDescription cdesc;
+    if (config) cdesc = *config;
+    sp<Entry> e = c->getEntries().valueFor(cdesc);
+    if (c == NULL) {
+        fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
+        return NULL;
+    }
+    
+    return e;
+}
+
+const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
+{
+    sp<const Entry> e = getEntry(resID);
+    if (e == NULL) {
+        return NULL;
+    }
+
+    const size_t N = e->getBag().size();
+    for (size_t i=0; i<N; i++) {
+        const Item& it = e->getBag().valueAt(i);
+        if (it.bagKeyId == 0) {
+            fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
+                    String8(e->getName()).string(),
+                    String8(e->getBag().keyAt(i)).string());
+        }
+        if (it.bagKeyId == attrID) {
+            return &it;
+        }
+    }
+
+    return NULL;
+}
+
+bool ResourceTable::getItemValue(
+    uint32_t resID, uint32_t attrID, Res_value* outValue)
+{
+    const Item* item = getItem(resID, attrID);
+
+    bool res = false;
+    if (item != NULL) {
+        if (item->evaluating) {
+            sp<const Entry> e = getEntry(resID);
+            const size_t N = e->getBag().size();
+            size_t i;
+            for (i=0; i<N; i++) {
+                if (&e->getBag().valueAt(i) == item) {
+                    break;
+                }
+            }
+            fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
+                    String8(e->getName()).string(),
+                    String8(e->getBag().keyAt(i)).string());
+            return false;
+        }
+        item->evaluating = true;
+        res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
+        NOISY(
+            if (res) {
+                printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
+                       resID, attrID, String8(getEntry(resID)->getName()).string(),
+                       outValue->dataType, outValue->data);
+            } else {
+                printf("getItemValue of #%08x[#%08x]: failed\n",
+                       resID, attrID);
+            }
+        );
+        item->evaluating = false;
+    }
+    return res;
+}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
new file mode 100644
index 0000000..e8fbd9b
--- /dev/null
+++ b/tools/aapt/ResourceTable.h
@@ -0,0 +1,534 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef RESOURCE_TABLE_H
+#define RESOURCE_TABLE_H
+
+#include "StringPool.h"
+#include "SourcePos.h"
+
+#include <set>
+#include <map>
+
+using namespace std;
+
+class ResourceTable;
+
+enum {
+    XML_COMPILE_STRIP_COMMENTS = 1<<0,
+    XML_COMPILE_ASSIGN_ATTRIBUTE_IDS = 1<<1,
+    XML_COMPILE_COMPACT_WHITESPACE = 1<<2,
+    XML_COMPILE_STRIP_WHITESPACE = 1<<3,
+    XML_COMPILE_STRIP_RAW_VALUES = 1<<4,
+    
+    XML_COMPILE_STANDARD_RESOURCE =
+            XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
+            | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES
+};
+
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+                        const sp<AaptFile>& target,
+                        ResourceTable* table,
+                        int options = XML_COMPILE_STANDARD_RESOURCE);
+
+status_t compileResourceFile(Bundle* bundle,
+                             const sp<AaptAssets>& assets,
+                             const sp<AaptFile>& in,
+                             const ResTable_config& defParams,
+                             const bool overwrite,
+                             ResourceTable* outTable);
+
+struct AccessorCookie
+{
+    SourcePos sourcePos;
+    String8 attr;
+    String8 value;
+
+    AccessorCookie(const SourcePos&p, const String8& a, const String8& v)
+        :sourcePos(p),
+         attr(a),
+         value(v)
+    {
+    }
+};
+
+class ResourceTable : public ResTable::Accessor
+{
+public:
+    class Package;
+    class Type;
+    class Entry;
+
+    ResourceTable(Bundle* bundle, const String16& assetsPackage);
+
+    status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
+
+    status_t addPublic(const SourcePos& pos,
+                       const String16& package,
+                       const String16& type,
+                       const String16& name,
+                       const uint32_t ident);
+
+    status_t addEntry(const SourcePos& pos,
+                      const String16& package,
+                      const String16& type,
+                      const String16& name,
+                      const String16& value,
+                      const Vector<StringPool::entry_style_span>* style = NULL,
+                      const ResTable_config* params = NULL,
+                      const bool doSetIndex = false,
+                      const int32_t format = ResTable_map::TYPE_ANY,
+                      const bool overwrite = false);
+
+    status_t startBag(const SourcePos& pos,
+                    const String16& package,
+                    const String16& type,
+                    const String16& name,
+                    const String16& bagParent,
+                    const ResTable_config* params = NULL,
+                    bool replace = false,
+                    bool isId = false);
+    
+    status_t addBag(const SourcePos& pos,
+                    const String16& package,
+                    const String16& type,
+                    const String16& name,
+                    const String16& bagParent,
+                    const String16& bagKey,
+                    const String16& value,
+                    const Vector<StringPool::entry_style_span>* style = NULL,
+                    const ResTable_config* params = NULL,
+                    bool replace = false,
+                    bool isId = false,
+                    const int32_t format = ResTable_map::TYPE_ANY);
+
+    bool hasBagOrEntry(const String16& package,
+                       const String16& type,
+                       const String16& name) const;
+
+    bool hasBagOrEntry(const String16& ref,
+                       const String16* defType = NULL,
+                       const String16* defPackage = NULL);
+
+    bool appendComment(const String16& package,
+                       const String16& type,
+                       const String16& name,
+                       const String16& comment,
+                       bool onlyIfEmpty = false);
+
+    bool appendTypeComment(const String16& package,
+                           const String16& type,
+                           const String16& name,
+                           const String16& comment);
+    
+    size_t size() const;
+    size_t numLocalResources() const;
+    bool hasResources() const;
+
+    sp<AaptFile> flatten(Bundle*);
+
+    static inline uint32_t makeResId(uint32_t packageId,
+                                     uint32_t typeId,
+                                     uint32_t nameId)
+    {
+        return nameId | (typeId<<16) | (packageId<<24);
+    }
+
+    static inline uint32_t getResId(const sp<Package>& p,
+                                    const sp<Type>& t,
+                                    uint32_t nameId);
+
+    uint32_t getResId(const String16& package,
+                      const String16& type,
+                      const String16& name,
+                      bool onlyPublic = false) const;
+
+    uint32_t getResId(const String16& ref,
+                      const String16* defType = NULL,
+                      const String16* defPackage = NULL,
+                      const char** outErrorMsg = NULL,
+                      bool onlyPublic = false) const;
+
+    static bool isValidResourceName(const String16& s);
+    
+    bool stringToValue(Res_value* outValue, StringPool* pool,
+                       const String16& str,
+                       bool preserveSpaces, bool coerceType,
+                       uint32_t attrID,
+                       const Vector<StringPool::entry_style_span>* style = NULL,
+                       String16* outStr = NULL, void* accessorCookie = NULL,
+                       uint32_t attrType = ResTable_map::TYPE_ANY);
+
+    status_t assignResourceIds();
+    status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
+    void addLocalization(const String16& name, const String8& locale);
+    status_t validateLocalizations(void);
+
+    status_t flatten(Bundle*, const sp<AaptFile>& dest);
+
+    void writePublicDefinitions(const String16& package, FILE* fp);
+
+    virtual uint32_t getCustomResource(const String16& package,
+                                       const String16& type,
+                                       const String16& name) const;
+    virtual uint32_t getCustomResourceWithCreation(const String16& package,
+                                                   const String16& type,
+                                                   const String16& name,
+                                                   const bool createIfNeeded);
+    virtual uint32_t getRemappedPackage(uint32_t origPackage) const;
+    virtual bool getAttributeType(uint32_t attrID, uint32_t* outType);
+    virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin);
+    virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax);
+    virtual bool getAttributeKeys(uint32_t attrID, Vector<String16>* outKeys);
+    virtual bool getAttributeEnum(uint32_t attrID,
+                                  const char16_t* name, size_t nameLen,
+                                  Res_value* outValue);
+    virtual bool getAttributeFlags(uint32_t attrID,
+                                   const char16_t* name, size_t nameLen,
+                                   Res_value* outValue);
+    virtual uint32_t getAttributeL10N(uint32_t attrID);
+
+    virtual bool getLocalizationSetting();
+    virtual void reportError(void* accessorCookie, const char* fmt, ...);
+
+    void setCurrentXmlPos(const SourcePos& pos) { mCurrentXmlPos = pos; }
+
+    class Item {
+    public:
+        Item() : isId(false), format(ResTable_map::TYPE_ANY), bagKeyId(0), evaluating(false)
+            { memset(&parsedValue, 0, sizeof(parsedValue)); }
+        Item(const SourcePos& pos,
+             bool _isId,
+             const String16& _value,
+             const Vector<StringPool::entry_style_span>* _style = NULL,
+             int32_t format = ResTable_map::TYPE_ANY);
+        Item(const Item& o) : sourcePos(o.sourcePos),
+            isId(o.isId), value(o.value), style(o.style),
+            format(o.format), bagKeyId(o.bagKeyId), evaluating(false) {
+            memset(&parsedValue, 0, sizeof(parsedValue));
+        }
+        ~Item() { }
+
+        Item& operator=(const Item& o) {
+            sourcePos = o.sourcePos;
+            isId = o.isId;
+            value = o.value;
+            style = o.style;
+            format = o.format;
+            bagKeyId = o.bagKeyId;
+            parsedValue = o.parsedValue;
+            return *this;
+        }
+
+        SourcePos                               sourcePos;
+        mutable bool                            isId;
+        String16                                value;
+        Vector<StringPool::entry_style_span>    style;
+        int32_t                                 format;
+        uint32_t                                bagKeyId;
+        mutable bool                            evaluating;
+        Res_value                               parsedValue;
+    };
+
+    class Entry : public RefBase {
+    public:
+        Entry(const String16& name, const SourcePos& pos)
+            : mName(name), mType(TYPE_UNKNOWN),
+              mItemFormat(ResTable_map::TYPE_ANY), mNameIndex(-1), mPos(pos)
+        { }
+        virtual ~Entry() { }
+
+        enum type {
+            TYPE_UNKNOWN = 0,
+            TYPE_ITEM,
+            TYPE_BAG
+        };
+        
+        String16 getName() const { return mName; }
+        type getType() const { return mType; }
+
+        void setParent(const String16& parent) { mParent = parent; }
+        String16 getParent() const { return mParent; }
+
+        status_t makeItABag(const SourcePos& sourcePos);
+        
+        status_t setItem(const SourcePos& pos,
+                         const String16& value,
+                         const Vector<StringPool::entry_style_span>* style = NULL,
+                         int32_t format = ResTable_map::TYPE_ANY,
+                         const bool overwrite = false);
+
+        status_t addToBag(const SourcePos& pos,
+                          const String16& key, const String16& value,
+                          const Vector<StringPool::entry_style_span>* style = NULL,
+                          bool replace=false, bool isId = false,
+                          int32_t format = ResTable_map::TYPE_ANY);
+
+        // Index of the entry's name string in the key pool.
+        int32_t getNameIndex() const { return mNameIndex; }
+        void setNameIndex(int32_t index) { mNameIndex = index; }
+
+        const Item* getItem() const { return mType == TYPE_ITEM ? &mItem : NULL; }
+        const KeyedVector<String16, Item>& getBag() const { return mBag; }
+
+        status_t generateAttributes(ResourceTable* table,
+                                    const String16& package);
+
+        status_t assignResourceIds(ResourceTable* table,
+                                   const String16& package);
+
+        status_t prepareFlatten(StringPool* strings, ResourceTable* table);
+
+        ssize_t flatten(Bundle*, const sp<AaptFile>& data, bool isPublic);
+
+        const SourcePos& getPos() const { return mPos; }
+
+    private:
+        String16 mName;
+        String16 mParent;
+        type mType;
+        Item mItem;
+        int32_t mItemFormat;
+        KeyedVector<String16, Item> mBag;
+        int32_t mNameIndex;
+        uint32_t mParentId;
+        SourcePos mPos;
+    };
+
+    struct ConfigDescription : public ResTable_config {
+        ConfigDescription() {
+            memset(this, 0, sizeof(*this));
+            size = sizeof(ResTable_config);
+        }
+        ConfigDescription(const ResTable_config&o) {
+            *static_cast<ResTable_config*>(this) = o;
+            size = sizeof(ResTable_config);
+        }
+        ConfigDescription(const ConfigDescription&o) {
+            *static_cast<ResTable_config*>(this) = o;
+        }
+        
+        ConfigDescription& operator=(const ResTable_config& o) {
+            *static_cast<ResTable_config*>(this) = o;
+            size = sizeof(ResTable_config);
+            return *this;
+        }
+        ConfigDescription& operator=(const ConfigDescription& o) {
+            *static_cast<ResTable_config*>(this) = o;
+            return *this;
+        }
+        
+        inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
+        inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
+        inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
+        inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
+        inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
+        inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
+    };
+    
+    class ConfigList : public RefBase {
+    public:
+        ConfigList(const String16& name, const SourcePos& pos)
+            : mName(name), mPos(pos), mPublic(false), mEntryIndex(-1) { }
+        virtual ~ConfigList() { }
+        
+        String16 getName() const { return mName; }
+        const SourcePos& getPos() const { return mPos; }
+        
+        void appendComment(const String16& comment, bool onlyIfEmpty = false);
+        const String16& getComment() const { return mComment; }
+        
+        void appendTypeComment(const String16& comment);
+        const String16& getTypeComment() const { return mTypeComment; }
+        
+        // Index of this entry in its Type.
+        int32_t getEntryIndex() const { return mEntryIndex; }
+        void setEntryIndex(int32_t index) { mEntryIndex = index; }
+        
+        void setPublic(bool pub) { mPublic = pub; }
+        bool getPublic() const { return mPublic; }
+        void setPublicSourcePos(const SourcePos& pos) { mPublicSourcePos = pos; }
+        const SourcePos& getPublicSourcePos() { return mPublicSourcePos; }
+        
+        void addEntry(const ResTable_config& config, const sp<Entry>& entry) {
+            mEntries.add(config, entry);
+        }
+        
+        const DefaultKeyedVector<ConfigDescription, sp<Entry> >& getEntries() const { return mEntries; }
+    private:
+        const String16 mName;
+        const SourcePos mPos;
+        String16 mComment;
+        String16 mTypeComment;
+        bool mPublic;
+        SourcePos mPublicSourcePos;
+        int32_t mEntryIndex;
+        DefaultKeyedVector<ConfigDescription, sp<Entry> > mEntries;
+    };
+    
+    class Public {
+    public:
+        Public() : sourcePos(), ident(0) { }
+        Public(const SourcePos& pos,
+               const String16& _comment,
+               uint32_t _ident)
+            : sourcePos(pos),
+            comment(_comment), ident(_ident) { }
+        Public(const Public& o) : sourcePos(o.sourcePos),
+            comment(o.comment), ident(o.ident) { }
+        ~Public() { }
+        
+        Public& operator=(const Public& o) {
+            sourcePos = o.sourcePos;
+            comment = o.comment;
+            ident = o.ident;
+            return *this;
+        }
+        
+        SourcePos   sourcePos;
+        String16    comment;
+        uint32_t    ident;
+    };
+    
+    class Type : public RefBase {
+    public:
+        Type(const String16& name, const SourcePos& pos)
+                : mName(name), mFirstPublicSourcePos(NULL), mPublicIndex(-1), mIndex(-1), mPos(pos)
+        { }
+        virtual ~Type() { delete mFirstPublicSourcePos; }
+
+        status_t addPublic(const SourcePos& pos,
+                           const String16& name,
+                           const uint32_t ident);
+
+        String16 getName() const { return mName; }
+        sp<Entry> getEntry(const String16& entry,
+                           const SourcePos& pos,
+                           const ResTable_config* config = NULL,
+                           bool doSetIndex = false);
+
+        const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; }
+
+        int32_t getPublicIndex() const { return mPublicIndex; }
+
+        int32_t getIndex() const { return mIndex; }
+        void setIndex(int32_t index) { mIndex = index; }
+
+        status_t applyPublicEntryOrder();
+
+        const SortedVector<ConfigDescription>& getUniqueConfigs() const { return mUniqueConfigs; }
+        
+        const DefaultKeyedVector<String16, sp<ConfigList> >& getConfigs() const { return mConfigs; }
+        const Vector<sp<ConfigList> >& getOrderedConfigs() const { return mOrderedConfigs; }
+
+        const SourcePos& getPos() const { return mPos; }
+    private:
+        String16 mName;
+        SourcePos* mFirstPublicSourcePos;
+        DefaultKeyedVector<String16, Public> mPublic;
+        SortedVector<ConfigDescription> mUniqueConfigs;
+        DefaultKeyedVector<String16, sp<ConfigList> > mConfigs;
+        Vector<sp<ConfigList> > mOrderedConfigs;
+        int32_t mPublicIndex;
+        int32_t mIndex;
+        SourcePos mPos;
+    };
+
+    class Package : public RefBase {
+    public:
+        Package(const String16& name, ssize_t includedId=-1);
+        virtual ~Package() { }
+
+        String16 getName() const { return mName; }
+        sp<Type> getType(const String16& type,
+                         const SourcePos& pos,
+                         bool doSetIndex = false);
+
+        ssize_t getAssignedId() const { return mIncludedId; }
+
+        const ResStringPool& getTypeStrings() const { return mTypeStrings; }
+        uint32_t indexOfTypeString(const String16& s) const { return mTypeStringsMapping.valueFor(s); }
+        const sp<AaptFile> getTypeStringsData() const { return mTypeStringsData; }
+        status_t setTypeStrings(const sp<AaptFile>& data);
+
+        const ResStringPool& getKeyStrings() const { return mKeyStrings; }
+        uint32_t indexOfKeyString(const String16& s) const { return mKeyStringsMapping.valueFor(s); }
+        const sp<AaptFile> getKeyStringsData() const { return mKeyStringsData; }
+        status_t setKeyStrings(const sp<AaptFile>& data);
+
+        status_t applyPublicTypeOrder();
+
+        const DefaultKeyedVector<String16, sp<Type> >& getTypes() const { return mTypes; }
+        const Vector<sp<Type> >& getOrderedTypes() const { return mOrderedTypes; }
+
+    private:
+        status_t setStrings(const sp<AaptFile>& data,
+                            ResStringPool* strings,
+                            DefaultKeyedVector<String16, uint32_t>* mappings);
+
+        const String16 mName;
+        const ssize_t mIncludedId;
+        DefaultKeyedVector<String16, sp<Type> > mTypes;
+        Vector<sp<Type> > mOrderedTypes;
+        sp<AaptFile> mTypeStringsData;
+        sp<AaptFile> mKeyStringsData;
+        ResStringPool mTypeStrings;
+        ResStringPool mKeyStrings;
+        DefaultKeyedVector<String16, uint32_t> mTypeStringsMapping;
+        DefaultKeyedVector<String16, uint32_t> mKeyStringsMapping;
+    };
+
+private:
+    void writePublicDefinitions(const String16& package, FILE* fp, bool pub);
+    sp<Package> getPackage(const String16& package);
+    sp<Type> getType(const String16& package,
+                     const String16& type,
+                     const SourcePos& pos,
+                     bool doSetIndex = false);
+    sp<Entry> getEntry(const String16& package,
+                       const String16& type,
+                       const String16& name,
+                       const SourcePos& pos,
+                       const ResTable_config* config = NULL,
+                       bool doSetIndex = false);
+    sp<const Entry> getEntry(uint32_t resID,
+                             const ResTable_config* config = NULL) const;
+    const Item* getItem(uint32_t resID, uint32_t attrID) const;
+    bool getItemValue(uint32_t resID, uint32_t attrID,
+                      Res_value* outValue);
+
+
+    String16 mAssetsPackage;
+    sp<AaptAssets> mAssets;
+    DefaultKeyedVector<String16, sp<Package> > mPackages;
+    Vector<sp<Package> > mOrderedPackages;
+    uint32_t mNextPackageId;
+    bool mHaveAppPackage;
+    bool mIsAppPackage;
+    size_t mNumLocal;
+    SourcePos mCurrentXmlPos;
+    Bundle* mBundle;
+    
+    // key = string resource name, value = set of locales in which that name is defined
+    map<String16, set<String8> > mLocalizations;
+};
+
+class ResourceFilter
+{
+public:
+    ResourceFilter() : mData(), mContainsPseudo(false) {}
+    status_t parse(const char* arg);
+    bool match(int axis, uint32_t value);
+    bool match(const ResTable_config& config);
+    inline bool containsPseudo() { return mContainsPseudo; }
+
+private:
+    KeyedVector<int,SortedVector<uint32_t> > mData;
+    bool mContainsPseudo;
+};
+
+
+#endif
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
new file mode 100644
index 0000000..2761d18
--- /dev/null
+++ b/tools/aapt/SourcePos.cpp
@@ -0,0 +1,171 @@
+#include "SourcePos.h"
+
+#include <stdarg.h>
+#include <vector>
+
+using namespace std;
+
+
+// ErrorPos
+// =============================================================================
+struct ErrorPos
+{
+    String8 file;
+    int line;
+    String8 error;
+    bool fatal;
+
+    ErrorPos();
+    ErrorPos(const ErrorPos& that);
+    ErrorPos(const String8& file, int line, const String8& error, bool fatal);
+    ~ErrorPos();
+    bool operator<(const ErrorPos& rhs) const;
+    bool operator==(const ErrorPos& rhs) const;
+    ErrorPos& operator=(const ErrorPos& rhs);
+
+    void print(FILE* to) const;
+};
+
+static vector<ErrorPos> g_errors;
+
+ErrorPos::ErrorPos()
+    :line(-1), fatal(false)
+{
+}
+
+ErrorPos::ErrorPos(const ErrorPos& that)
+    :file(that.file),
+     line(that.line),
+     error(that.error),
+     fatal(that.fatal)
+{
+}
+
+ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat)
+    :file(f),
+     line(l),
+     error(e),
+     fatal(fat)
+{
+}
+
+ErrorPos::~ErrorPos()
+{
+}
+
+bool
+ErrorPos::operator<(const ErrorPos& rhs) const
+{
+    if (this->file < rhs.file) return true;
+    if (this->file == rhs.file) {
+        if (this->line < rhs.line) return true;
+        if (this->line == rhs.line) {
+            if (this->error < rhs.error) return true;
+        }
+    }
+    return false;
+}
+
+bool
+ErrorPos::operator==(const ErrorPos& rhs) const
+{
+    return this->file == rhs.file
+            && this->line == rhs.line
+            && this->error == rhs.error;
+}
+
+ErrorPos&
+ErrorPos::operator=(const ErrorPos& rhs)
+{
+    this->file = rhs.file;
+    this->line = rhs.line;
+    this->error = rhs.error;
+    return *this;
+}
+
+void
+ErrorPos::print(FILE* to) const
+{
+    const char* type = fatal ? "ERROR" : "WARNING";
+    
+    if (this->line >= 0) {
+        fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
+    } else {
+        fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string());
+    }
+}
+
+// SourcePos
+// =============================================================================
+SourcePos::SourcePos(const String8& f, int l)
+    : file(f), line(l)
+{
+}
+
+SourcePos::SourcePos(const SourcePos& that)
+    : file(that.file), line(that.line)
+{
+}
+
+SourcePos::SourcePos()
+    : file("???", 0), line(-1)
+{
+}
+
+SourcePos::~SourcePos()
+{
+}
+
+int
+SourcePos::error(const char* fmt, ...) const
+{
+    int retval=0;
+    char buf[1024];
+    va_list ap;
+    va_start(ap, fmt);
+    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    char* p = buf + retval - 1;
+    while (p > buf && *p == '\n') {
+        *p = '\0';
+        p--;
+    }
+    g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true));
+    return retval;
+}
+
+int
+SourcePos::warning(const char* fmt, ...) const
+{
+    int retval=0;
+    char buf[1024];
+    va_list ap;
+    va_start(ap, fmt);
+    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    char* p = buf + retval - 1;
+    while (p > buf && *p == '\n') {
+        *p = '\0';
+        p--;
+    }
+    ErrorPos(this->file, this->line, String8(buf), false).print(stderr);
+    return retval;
+}
+
+bool
+SourcePos::hasErrors()
+{
+    return g_errors.size() > 0;
+}
+
+void
+SourcePos::printErrors(FILE* to)
+{
+    vector<ErrorPos>::const_iterator it;
+    for (it=g_errors.begin(); it!=g_errors.end(); it++) {
+        it->print(to);
+    }
+}
+
+
+
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
new file mode 100644
index 0000000..33f72a9
--- /dev/null
+++ b/tools/aapt/SourcePos.h
@@ -0,0 +1,28 @@
+#ifndef SOURCEPOS_H
+#define SOURCEPOS_H
+
+#include <utils/String8.h>
+#include <stdio.h>
+
+using namespace android;
+
+class SourcePos
+{
+public:
+    String8 file;
+    int line;
+
+    SourcePos(const String8& f, int l);
+    SourcePos(const SourcePos& that);
+    SourcePos();
+    ~SourcePos();
+
+    int error(const char* fmt, ...) const;
+    int warning(const char* fmt, ...) const;
+
+    static bool hasErrors();
+    static void printErrors(FILE* to);
+};
+
+
+#endif // SOURCEPOS_H
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
new file mode 100644
index 0000000..878d3b1
--- /dev/null
+++ b/tools/aapt/StringPool.cpp
@@ -0,0 +1,369 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "StringPool.h"
+
+#include <utils/ByteOrder.h>
+
+#define NOISY(x) //x
+
+void strcpy16_htod(uint16_t* dst, const uint16_t* src)
+{
+    while (*src) {
+        char16_t s = htods(*src);
+        *dst++ = s;
+        src++;
+    }
+    *dst = 0;
+}
+
+void printStringPool(const ResStringPool* pool)
+{
+    const size_t NS = pool->size();
+    for (size_t s=0; s<NS; s++) {
+        size_t len;
+        printf("String #%d: %s\n", s,
+                String8(pool->stringAt(s, &len)).string());
+    }
+}
+
+StringPool::StringPool(bool sorted)
+    : mSorted(sorted), mValues(-1), mIdents(-1)
+{
+}
+
+ssize_t StringPool::add(const String16& value, bool mergeDuplicates)
+{
+    return add(String16(), value, mergeDuplicates);
+}
+
+ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans)
+{
+    ssize_t res = add(String16(), value, false);
+    if (res >= 0) {
+        addStyleSpans(res, spans);
+    }
+    return res;
+}
+
+ssize_t StringPool::add(const String16& ident, const String16& value,
+                        bool mergeDuplicates)
+{
+    if (ident.size() > 0) {
+        ssize_t idx = mIdents.valueFor(ident);
+        if (idx >= 0) {
+            fprintf(stderr, "ERROR: Duplicate string identifier %s\n",
+                    String8(mEntries[idx].value).string());
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    ssize_t vidx = mValues.indexOfKey(value);
+    ssize_t pos = vidx >= 0 ? mValues.valueAt(vidx) : -1;
+    ssize_t eidx = pos >= 0 ? mEntryArray.itemAt(pos) : -1;
+    if (eidx < 0) {
+        eidx = mEntries.add(entry(value));
+        if (eidx < 0) {
+            fprintf(stderr, "Failure adding string %s\n", String8(value).string());
+            return eidx;
+        }
+    }
+
+    const bool first = vidx < 0;
+    if (first || !mergeDuplicates) {
+        pos = mEntryArray.add(eidx);
+        if (first) {
+            vidx = mValues.add(value, pos);
+            const size_t N = mEntryArrayToValues.size();
+            for (size_t i=0; i<N; i++) {
+                size_t& e = mEntryArrayToValues.editItemAt(i);
+                if ((ssize_t)e >= vidx) {
+                    e++;
+                }
+            }
+        }
+        mEntryArrayToValues.add(vidx);
+        if (!mSorted) {
+            entry& ent = mEntries.editItemAt(eidx);
+            ent.indices.add(pos);
+        }
+    }
+
+    if (ident.size() > 0) {
+        mIdents.add(ident, vidx);
+    }
+
+    NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n",
+            String8(value).string(), pos, eidx, vidx));
+    
+    return pos;
+}
+
+status_t StringPool::addStyleSpan(size_t idx, const String16& name,
+                                  uint32_t start, uint32_t end)
+{
+    entry_style_span span;
+    span.name = name;
+    span.span.firstChar = start;
+    span.span.lastChar = end;
+    return addStyleSpan(idx, span);
+}
+
+status_t StringPool::addStyleSpans(size_t idx, const Vector<entry_style_span>& spans)
+{
+    const size_t N=spans.size();
+    for (size_t i=0; i<N; i++) {
+        status_t err = addStyleSpan(idx, spans[i]);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
+{
+    LOG_ALWAYS_FATAL_IF(mSorted, "Can't use styles with sorted string pools.");
+
+    // Place blank entries in the span array up to this index.
+    while (mEntryStyleArray.size() <= idx) {
+        mEntryStyleArray.add();
+    }
+
+    entry_style& style = mEntryStyleArray.editItemAt(idx);
+    style.spans.add(span);
+    return NO_ERROR;
+}
+
+size_t StringPool::size() const
+{
+    return mSorted ? mValues.size() : mEntryArray.size();
+}
+
+const StringPool::entry& StringPool::entryAt(size_t idx) const
+{
+    if (!mSorted) {
+        return mEntries[mEntryArray[idx]];
+    } else {
+        return mEntries[mEntryArray[mValues.valueAt(idx)]];
+    }
+}
+
+size_t StringPool::countIdentifiers() const
+{
+    return mIdents.size();
+}
+
+sp<AaptFile> StringPool::createStringBlock()
+{
+    sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),
+                                     String8());
+    status_t err = writeStringBlock(pool);
+    return err == NO_ERROR ? pool : NULL;
+}
+
+status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
+{
+    // Allow appending.  Sorry this is a little wacky.
+    if (pool->getSize() > 0) {
+        sp<AaptFile> block = createStringBlock();
+        if (block == NULL) {
+            return UNKNOWN_ERROR;
+        }
+        ssize_t res = pool->writeData(block->getData(), block->getSize());
+        return (res >= 0) ? (status_t)NO_ERROR : res;
+    }
+
+    // First we need to add all style span names to the string pool.
+    // We do this now (instead of when the span is added) so that these
+    // will appear at the end of the pool, not disrupting the order
+    // our client placed their own strings in it.
+    
+    const size_t STYLES = mEntryStyleArray.size();
+    size_t i;
+
+    for (i=0; i<STYLES; i++) {
+        entry_style& style = mEntryStyleArray.editItemAt(i);
+        const size_t N = style.spans.size();
+        for (size_t i=0; i<N; i++) {
+            entry_style_span& span = style.spans.editItemAt(i);
+            ssize_t idx = add(span.name, true);
+            if (idx < 0) {
+                fprintf(stderr, "Error adding span for style tag '%s'\n",
+                        String8(span.name).string());
+                return idx;
+            }
+            span.span.name.index = (uint32_t)idx;
+        }
+    }
+
+    const size_t ENTRIES = size();
+
+    // Now build the pool of unique strings.
+
+    const size_t STRINGS = mEntries.size();
+    const size_t preSize = sizeof(ResStringPool_header)
+                         + (sizeof(uint32_t)*ENTRIES)
+                         + (sizeof(uint32_t)*STYLES);
+    if (pool->editData(preSize) == NULL) {
+        fprintf(stderr, "ERROR: Out of memory for string pool\n");
+        return NO_MEMORY;
+    }
+
+    size_t strPos = 0;
+    for (i=0; i<STRINGS; i++) {
+        entry& ent = mEntries.editItemAt(i);
+        const size_t strSize = (ent.value.size());
+        const size_t lenSize = strSize > 0x7fff ? sizeof(uint32_t) : sizeof(uint16_t);
+        const size_t totalSize = lenSize + ((strSize+1)*sizeof(uint16_t));
+
+        ent.offset = strPos;
+        uint16_t* dat = (uint16_t*)pool->editData(preSize + strPos + totalSize);
+        if (dat == NULL) {
+            fprintf(stderr, "ERROR: Out of memory for string pool\n");
+            return NO_MEMORY;
+        }
+        dat += (preSize+strPos)/sizeof(uint16_t);
+        if (lenSize > sizeof(uint16_t)) {
+            *dat = htods(0x8000 | ((strSize>>16)&0x7ffff));
+            dat++;
+        }
+        *dat++ = htods(strSize);
+        strcpy16_htod(dat, ent.value);
+
+        strPos += lenSize + (strSize+1)*sizeof(uint16_t);
+    }
+
+    // Pad ending string position up to a uint32_t boundary.
+
+    if (strPos&0x3) {
+        size_t padPos = ((strPos+3)&~0x3);
+        uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos);
+        if (dat == NULL) {
+            fprintf(stderr, "ERROR: Out of memory padding string pool\n");
+            return NO_MEMORY;
+        }
+        memset(dat+preSize+strPos, 0, padPos-strPos);
+        strPos = padPos;
+    }
+
+    // Build the pool of style spans.
+
+    size_t styPos = strPos;
+    for (i=0; i<STYLES; i++) {
+        entry_style& ent = mEntryStyleArray.editItemAt(i);
+        const size_t N = ent.spans.size();
+        const size_t totalSize = (N*sizeof(ResStringPool_span))
+                               + sizeof(ResStringPool_ref);
+
+        ent.offset = styPos-strPos;
+        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize);
+        if (dat == NULL) {
+            fprintf(stderr, "ERROR: Out of memory for string styles\n");
+            return NO_MEMORY;
+        }
+        ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos);
+        for (size_t i=0; i<N; i++) {
+            span->name.index = htodl(ent.spans[i].span.name.index);
+            span->firstChar = htodl(ent.spans[i].span.firstChar);
+            span->lastChar = htodl(ent.spans[i].span.lastChar);
+            span++;
+        }
+        span->name.index = htodl(ResStringPool_span::END);
+
+        styPos += totalSize;
+    }
+
+    if (STYLES > 0) {
+        // Add full terminator at the end (when reading we validate that
+        // the end of the pool is fully terminated to simplify error
+        // checking).
+        size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref);
+        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra);
+        if (dat == NULL) {
+            fprintf(stderr, "ERROR: Out of memory for string styles\n");
+            return NO_MEMORY;
+        }
+        uint32_t* p = (uint32_t*)(dat+preSize+styPos);
+        while (extra > 0) {
+            *p++ = htodl(ResStringPool_span::END);
+            extra -= sizeof(uint32_t);
+        }
+        styPos += extra;
+    }
+
+    // Write header.
+
+    ResStringPool_header* header =
+        (ResStringPool_header*)pool->padData(sizeof(uint32_t));
+    if (header == NULL) {
+        fprintf(stderr, "ERROR: Out of memory for string pool\n");
+        return NO_MEMORY;
+    }
+    memset(header, 0, sizeof(*header));
+    header->header.type = htods(RES_STRING_POOL_TYPE);
+    header->header.headerSize = htods(sizeof(*header));
+    header->header.size = htodl(pool->getSize());
+    header->stringCount = htodl(ENTRIES);
+    header->styleCount = htodl(STYLES);
+    if (mSorted) {
+        header->flags |= htodl(ResStringPool_header::SORTED_FLAG);
+    }
+    header->stringsStart = htodl(preSize);
+    header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
+
+    // Write string index array.
+
+    uint32_t* index = (uint32_t*)(header+1);
+    if (mSorted) {
+        for (i=0; i<ENTRIES; i++) {
+            entry& ent = const_cast<entry&>(entryAt(i));
+            ent.indices.clear();
+            ent.indices.add(i);
+            *index++ = htodl(ent.offset);
+        }
+    } else {
+        for (i=0; i<ENTRIES; i++) {
+            entry& ent = mEntries.editItemAt(mEntryArray[i]);
+            *index++ = htodl(ent.offset);
+            NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i,
+                    String8(ent.value).string(),
+                    mEntryArray[i], ent.offset));
+        }
+    }
+
+    // Write style index array.
+
+    if (mSorted) {
+        for (i=0; i<STYLES; i++) {
+            LOG_ALWAYS_FATAL("Shouldn't be here!");
+        }
+    } else {
+        for (i=0; i<STYLES; i++) {
+            *index++ = htodl(mEntryStyleArray[i].offset);
+        }
+    }
+
+    return NO_ERROR;
+}
+
+ssize_t StringPool::offsetForString(const String16& val) const
+{
+    const Vector<size_t>* indices = offsetsForString(val);
+    ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
+    NOISY(printf("Offset for string %s: %d (%s)\n", String8(val).string(), res,
+            res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8()));
+    return res;
+}
+
+const Vector<size_t>* StringPool::offsetsForString(const String16& val) const
+{
+    ssize_t pos = mValues.valueFor(val);
+    if (pos < 0) {
+        return NULL;
+    }
+    return &mEntries[mEntryArray[pos]].indices;
+}
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
new file mode 100644
index 0000000..9082b37
--- /dev/null
+++ b/tools/aapt/StringPool.h
@@ -0,0 +1,148 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef STRING_POOL_H
+#define STRING_POOL_H
+
+#include "Main.h"
+#include "AaptAssets.h"
+
+#include <utils/ResourceTypes.h>
+#include <utils/String16.h>
+#include <utils/TextOutput.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <expat.h>
+
+using namespace android;
+
+#define PRINT_STRING_METRICS 0
+
+void strcpy16_htod(uint16_t* dst, const uint16_t* src);
+
+void printStringPool(const ResStringPool* pool);
+
+/**
+ * The StringPool class is used as an intermediate representation for
+ * generating the string pool resource data structure that can be parsed with
+ * ResStringPool in include/utils/ResourceTypes.h.
+ */
+class StringPool
+{
+public:
+    struct entry {
+        entry() : offset(0) { }
+        entry(const String16& _value) : value(_value), offset(0) { }
+        entry(const entry& o) : value(o.value), offset(o.offset), indices(o.indices) { }
+
+        String16 value;
+        size_t offset;
+        Vector<size_t> indices;
+    };
+
+    struct entry_style_span {
+        String16 name;
+        ResStringPool_span span;
+    };
+
+    struct entry_style {
+        entry_style() : offset(0) { }
+
+        entry_style(const entry_style& o) : offset(o.offset), spans(o.spans) { }
+
+        size_t offset;
+        Vector<entry_style_span> spans;
+    };
+
+    /**
+     * If 'sorted' is true, then the final strings in the resource data
+     * structure will be generated in sorted order.  This allow for fast
+     * lookup with ResStringPool::indexOfString() (O(log n)), at the expense
+     * of support for styled string entries (which requires the same string
+     * be included multiple times in the pool).
+     */
+    explicit StringPool(bool sorted = false);
+
+    /**
+     * Add a new string to the pool.  If mergeDuplicates is true, thenif
+     * the string already exists the existing entry for it will be used;
+     * otherwise, or if the value doesn't already exist, a new entry is
+     * created.
+     *
+     * Returns the index in the entry array of the new string entry.  Note that
+     * if this string pool is sorted, the returned index will not be valid
+     * when the pool is finally written.
+     */
+    ssize_t add(const String16& value, bool mergeDuplicates = false);
+
+    ssize_t add(const String16& value, const Vector<entry_style_span>& spans);
+
+    ssize_t add(const String16& ident, const String16& value,
+                bool mergeDuplicates = false);
+
+    status_t addStyleSpan(size_t idx, const String16& name,
+                          uint32_t start, uint32_t end);
+    status_t addStyleSpans(size_t idx, const Vector<entry_style_span>& spans);
+    status_t addStyleSpan(size_t idx, const entry_style_span& span);
+
+    size_t size() const;
+
+    const entry& entryAt(size_t idx) const;
+
+    size_t countIdentifiers() const;
+
+    sp<AaptFile> createStringBlock();
+
+    status_t writeStringBlock(const sp<AaptFile>& pool);
+
+    /**
+     * Find out an offset in the pool for a particular string.  If the string
+     * pool is sorted, this can not be called until after createStringBlock()
+     * or writeStringBlock() has been called
+     * (which determines the offsets).  In the case of a string that appears
+     * multiple times in the pool, the first offset will be returned.  Returns
+     * -1 if the string does not exist.
+     */
+    ssize_t offsetForString(const String16& val) const;
+
+    /**
+     * Find all of the offsets in the pool for a particular string.  If the
+     * string pool is sorted, this can not be called until after
+     * createStringBlock() or writeStringBlock() has been called
+     * (which determines the offsets).  Returns NULL if the string does not exist.
+     */
+    const Vector<size_t>* offsetsForString(const String16& val) const;
+
+private:
+    const bool                              mSorted;
+    // Raw array of unique strings, in some arbitrary order.
+    Vector<entry>                           mEntries;
+    // Array of indices into mEntries, in the order they were
+    // added to the pool.  This can be different than mEntries
+    // if the same string was added multiple times (it will appear
+    // once in mEntries, with multiple occurrences in this array).
+    Vector<size_t>                          mEntryArray;
+    // Optional style span information associated with each index of
+    // mEntryArray.
+    Vector<entry_style>                     mEntryStyleArray;
+    // Mapping from indices in mEntryArray to indices in mValues.
+    Vector<size_t>                          mEntryArrayToValues;
+    // Unique set of all the strings added to the pool, mapped to
+    // the first index of mEntryArray where the value was added.
+    DefaultKeyedVector<String16, ssize_t>   mValues;
+    // Unique set of all (optional) identifiers of strings in the
+    // pool, mapping to indices in mEntries.
+    DefaultKeyedVector<String16, ssize_t>   mIdents;
+
+};
+
+#endif
+
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
new file mode 100644
index 0000000..d476567
--- /dev/null
+++ b/tools/aapt/XMLNode.cpp
@@ -0,0 +1,1295 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "XMLNode.h"
+#include "ResourceTable.h"
+
+#include <host/pseudolocalize.h>
+#include <utils/ByteOrder.h>
+#include <errno.h>
+#include <string.h>
+
+#ifndef HAVE_MS_C_RUNTIME
+#define O_BINARY 0
+#endif
+
+#define NOISY(x) //x
+#define NOISY_PARSE(x) //x
+
+const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/";
+const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
+const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/";
+
+const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
+const char* const ALLOWED_XLIFF_ELEMENTS[] = {
+        "bpt",
+        "ept",
+        "it",
+        "ph",
+        "g",
+        "bx",
+        "ex",
+        "x"
+    };
+
+bool isWhitespace(const char16_t* str)
+{
+    while (*str != 0 && *str < 128 && isspace(*str)) {
+        str++;
+    }
+    return *str == 0;
+}
+
+static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE);
+static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
+
+String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic)
+{
+    //printf("%s starts with %s?\n", String8(namespaceUri).string(),
+    //       String8(RESOURCES_PREFIX).string());
+    size_t prefixSize;
+    bool isPublic = true;
+    if (namespaceUri.startsWith(RESOURCES_PREFIX)) {
+        prefixSize = RESOURCES_PREFIX.size();
+    } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) {
+        isPublic = false;
+        prefixSize = RESOURCES_PRV_PREFIX.size();
+    } else {
+        if (outIsPublic) *outIsPublic = isPublic; // = true
+        return String16();
+    }
+
+    //printf("YES!\n");
+    //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string());
+    if (outIsPublic) *outIsPublic = isPublic;
+    return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize);
+}
+
+status_t parseStyledString(Bundle* bundle,
+                           const char* fileName,
+                           ResXMLTree* inXml,
+                           const String16& endTag,
+                           String16* outString,
+                           Vector<StringPool::entry_style_span>* outSpans,
+                           bool pseudolocalize)
+{
+    Vector<StringPool::entry_style_span> spanStack;
+    String16 curString;
+    String16 rawString;
+    const char* errorMsg;
+    int xliffDepth = 0;
+    bool firstTime = true;
+
+    size_t len;
+    ResXMLTree::event_code_t code;
+    while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+
+        if (code == ResXMLTree::TEXT) {
+            String16 text(inXml->getText(&len));
+            if (firstTime && text.size() > 0) {
+                firstTime = false;
+                if (text.string()[0] == '@') {
+                    // If this is a resource reference, don't do the pseudoloc.
+                    pseudolocalize = false;
+                }
+            }
+            if (xliffDepth == 0 && pseudolocalize) {
+                std::string orig(String8(text).string());
+                std::string pseudo = pseudolocalize_string(orig);
+                curString.append(String16(String8(pseudo.c_str())));
+            } else {
+                curString.append(text);
+            }
+        } else if (code == ResXMLTree::START_TAG) {
+            const String16 element16(inXml->getElementName(&len));
+            const String8 element8(element16);
+
+            size_t nslen;
+            const uint16_t* ns = inXml->getElementNamespace(&nslen);
+            if (ns == NULL) {
+                ns = (const uint16_t*)"\0\0";
+                nslen = 0;
+            }
+            const String8 nspace(String16(ns, nslen));
+            if (nspace == XLIFF_XMLNS) {
+                const int N = sizeof(ALLOWED_XLIFF_ELEMENTS)/sizeof(ALLOWED_XLIFF_ELEMENTS[0]);
+                for (int i=0; i<N; i++) {
+                    if (element8 == ALLOWED_XLIFF_ELEMENTS[i]) {
+                        xliffDepth++;
+                        // in this case, treat it like it was just text, in other words, do nothing
+                        // here and silently drop this element
+                        goto moveon;
+                    }
+                }
+                {
+                    SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                            "Found unsupported XLIFF tag <%s>\n",
+                            element8.string());
+                    return UNKNOWN_ERROR;
+                }
+moveon:
+                continue;
+            }
+
+            if (outSpans == NULL) {
+                SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                        "Found style tag <%s> where styles are not allowed\n", element8.string());
+                return UNKNOWN_ERROR;
+            }
+
+            if (!ResTable::collectString(outString, curString.string(),
+                                         curString.size(), false, &errorMsg, true)) {
+                SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
+                        errorMsg, String8(curString).string());
+                return UNKNOWN_ERROR;
+            }
+            rawString.append(curString);
+            curString = String16();
+
+            StringPool::entry_style_span span;
+            span.name = element16;
+            for (size_t ai=0; ai<inXml->getAttributeCount(); ai++) {
+                span.name.append(String16(";"));
+                const char16_t* str = inXml->getAttributeName(ai, &len);
+                span.name.append(str, len);
+                span.name.append(String16("="));
+                str = inXml->getAttributeStringValue(ai, &len);
+                span.name.append(str, len);
+            }
+            //printf("Span: %s\n", String8(span.name).string());
+            span.span.firstChar = span.span.lastChar = outString->size();
+            spanStack.push(span);
+
+        } else if (code == ResXMLTree::END_TAG) {
+            size_t nslen;
+            const uint16_t* ns = inXml->getElementNamespace(&nslen);
+            if (ns == NULL) {
+                ns = (const uint16_t*)"\0\0";
+                nslen = 0;
+            }
+            const String8 nspace(String16(ns, nslen));
+            if (nspace == XLIFF_XMLNS) {
+                xliffDepth--;
+                continue;
+            }
+            if (!ResTable::collectString(outString, curString.string(),
+                                         curString.size(), false, &errorMsg, true)) {
+                SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
+                        errorMsg, String8(curString).string());
+                return UNKNOWN_ERROR;
+            }
+            rawString.append(curString);
+            curString = String16();
+
+            if (spanStack.size() == 0) {
+                if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) {
+                    SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                            "Found tag %s where <%s> close is expected\n",
+                            String8(inXml->getElementName(&len)).string(),
+                            String8(endTag).string());
+                    return UNKNOWN_ERROR;
+                }
+                break;
+            }
+            StringPool::entry_style_span span = spanStack.top();
+            String16 spanTag;
+            ssize_t semi = span.name.findFirst(';');
+            if (semi >= 0) {
+                spanTag.setTo(span.name.string(), semi);
+            } else {
+                spanTag.setTo(span.name);
+            }
+            if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) {
+                SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                        "Found close tag %s where close tag %s is expected\n",
+                        String8(inXml->getElementName(&len)).string(),
+                        String8(spanTag).string());
+                return UNKNOWN_ERROR;
+            }
+            bool empty = true;
+            if (outString->size() > 0) {
+                span.span.lastChar = outString->size()-1;
+                if (span.span.lastChar >= span.span.firstChar) {
+                    empty = false;
+                    outSpans->add(span);
+                }
+            }
+            spanStack.pop();
+
+            if (empty) {
+                fprintf(stderr, "%s:%d: WARNING: empty '%s' span found in text '%s'\n",
+                        fileName, inXml->getLineNumber(),
+                        String8(spanTag).string(), String8(*outString).string());
+
+            }
+        } else if (code == ResXMLTree::START_NAMESPACE) {
+            // nothing
+        }
+    }
+
+    if (code == ResXMLTree::BAD_DOCUMENT) {
+            SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                    "Error parsing XML\n");
+    }
+
+    if (outSpans != NULL && outSpans->size() > 0) {
+        if (curString.size() > 0) {
+            if (!ResTable::collectString(outString, curString.string(),
+                                         curString.size(), false, &errorMsg, true)) {
+                SourcePos(String8(fileName), inXml->getLineNumber()).error(
+                        "%s (in %s)\n",
+                        errorMsg, String8(curString).string());
+                return UNKNOWN_ERROR;
+            }
+        }
+    } else {
+        // There is no style information, so string processing will happen
+        // later as part of the overall type conversion.  Return to the
+        // client the raw unprocessed text.
+        rawString.append(curString);
+        outString->setTo(rawString);
+    }
+
+    return NO_ERROR;
+}
+
+struct namespace_entry {
+    String8 prefix;
+    String8 uri;
+};
+
+static String8 make_prefix(int depth)
+{
+    String8 prefix;
+    int i;
+    for (i=0; i<depth; i++) {
+        prefix.append("  ");
+    }
+    return prefix;
+}
+
+static String8 build_namespace(const Vector<namespace_entry>& namespaces,
+        const uint16_t* ns)
+{
+    String8 str;
+    if (ns != NULL) {
+        str = String8(ns);
+        const size_t N = namespaces.size();
+        for (size_t i=0; i<N; i++) {
+            const namespace_entry& ne = namespaces.itemAt(i);
+            if (ne.uri == str) {
+                str = ne.prefix;
+                break;
+            }
+        }
+        str.append(":");
+    }
+    return str;
+}
+
+void printXMLBlock(ResXMLTree* block)
+{
+    block->restart();
+
+    Vector<namespace_entry> namespaces;
+    
+    ResXMLTree::event_code_t code;
+    int depth = 0;
+    while ((code=block->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        String8 prefix = make_prefix(depth);
+        int i;
+        if (code == ResXMLTree::START_TAG) {
+            size_t len;
+            const uint16_t* ns16 = block->getElementNamespace(&len);
+            String8 elemNs = build_namespace(namespaces, ns16);
+            const uint16_t* com16 = block->getComment(&len);
+            if (com16) {
+                printf("%s <!-- %s -->\n", prefix.string(), String8(com16).string());
+            }
+            printf("%sE: %s%s (line=%d)\n", prefix.string(), elemNs.string(),
+                   String8(block->getElementName(&len)).string(),
+                   block->getLineNumber());
+            int N = block->getAttributeCount();
+            depth++;
+            prefix = make_prefix(depth);
+            for (i=0; i<N; i++) {
+                uint32_t res = block->getAttributeNameResID(i);
+                ns16 = block->getAttributeNamespace(i, &len);
+                String8 ns = build_namespace(namespaces, ns16);
+                String8 name(block->getAttributeName(i, &len));
+                printf("%sA: ", prefix.string());
+                if (res) {
+                    printf("%s%s(0x%08x)", ns.string(), name.string(), res);
+                } else {
+                    printf("%s%s", ns.string(), name.string());
+                }
+                Res_value value;
+                block->getAttributeValue(i, &value);
+                if (value.dataType == Res_value::TYPE_NULL) {
+                    printf("=(null)");
+                } else if (value.dataType == Res_value::TYPE_REFERENCE) {
+                    printf("=@0x%x", (int)value.data);
+                } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+                    printf("=?0x%x", (int)value.data);
+                } else if (value.dataType == Res_value::TYPE_STRING) {
+                    printf("=\"%s\"",
+                           String8(block->getAttributeStringValue(i, &len)).string());
+                } else {
+                    printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
+                }
+                const char16_t* val = block->getAttributeStringValue(i, &len);
+                if (val != NULL) {
+                    printf(" (Raw: \"%s\")", String8(val).string());
+                }
+                printf("\n");
+            }
+        } else if (code == ResXMLTree::END_TAG) {
+            depth--;
+        } else if (code == ResXMLTree::START_NAMESPACE) {
+            namespace_entry ns;
+            size_t len;
+            const uint16_t* prefix16 = block->getNamespacePrefix(&len);
+            if (prefix16) {
+                ns.prefix = String8(prefix16);
+            } else {
+                ns.prefix = "<DEF>";
+            }
+            ns.uri = String8(block->getNamespaceUri(&len));
+            namespaces.push(ns);
+            printf("%sN: %s=%s\n", prefix.string(), ns.prefix.string(),
+                    ns.uri.string());
+            depth++;
+        } else if (code == ResXMLTree::END_NAMESPACE) {
+            depth--;
+            const namespace_entry& ns = namespaces.top();
+            size_t len;
+            const uint16_t* prefix16 = block->getNamespacePrefix(&len);
+            String8 pr;
+            if (prefix16) {
+                pr = String8(prefix16);
+            } else {
+                pr = "<DEF>";
+            }
+            if (ns.prefix != pr) {
+                prefix = make_prefix(depth);
+                printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n",
+                        prefix.string(), pr.string(), ns.prefix.string());
+            }
+            String8 uri = String8(block->getNamespaceUri(&len));
+            if (ns.uri != uri) {
+                prefix = make_prefix(depth);
+                printf("%s *** BAD END NS URI: found=%s, expected=%s\n",
+                        prefix.string(), uri.string(), ns.uri.string());
+            }
+            namespaces.pop();
+        } else if (code == ResXMLTree::TEXT) {
+            size_t len;
+            printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string());
+        }
+    }
+
+    block->restart();
+}
+
+status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree,
+                          bool stripAll, bool keepComments,
+                          const char** cDataTags)
+{
+    sp<XMLNode> root = XMLNode::parse(file);
+    if (root == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    root->removeWhitespace(stripAll, cDataTags);
+
+    NOISY(printf("Input XML from %s:\n", (const char*)file->getPrintableSource()));
+    NOISY(root->print());
+    sp<AaptFile> rsc = new AaptFile(String8(), AaptGroupEntry(), String8());
+    status_t err = root->flatten(rsc, !keepComments, false);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = outTree->setTo(rsc->getData(), rsc->getSize(), true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    NOISY(printf("Output XML:\n"));
+    NOISY(printXMLBlock(outTree));
+
+    return NO_ERROR;
+}
+
+sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file)
+{
+    char buf[16384];
+    int fd = open(file->getSourceFile().string(), O_RDONLY | O_BINARY);
+    if (fd < 0) {
+        SourcePos(file->getSourceFile(), -1).error("Unable to open file for read: %s",
+                strerror(errno));
+        return NULL;
+    }
+
+    XML_Parser parser = XML_ParserCreateNS(NULL, 1);
+    ParseState state;
+    state.filename = file->getPrintableSource();
+    state.parser = parser;
+    XML_SetUserData(parser, &state);
+    XML_SetElementHandler(parser, startElement, endElement);
+    XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
+    XML_SetCharacterDataHandler(parser, characterData);
+    XML_SetCommentHandler(parser, commentData);
+
+    ssize_t len;
+    bool done;
+    do {
+        len = read(fd, buf, sizeof(buf));
+        done = len < (ssize_t)sizeof(buf);
+        if (len < 0) {
+            SourcePos(file->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno));
+            close(fd);
+            return NULL;
+        }
+        if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
+            SourcePos(file->getSourceFile(), (int)XML_GetCurrentLineNumber(parser)).error(
+                    "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+            close(fd);
+            return NULL;
+        }
+    } while (!done);
+
+    XML_ParserFree(parser);
+    if (state.root == NULL) {
+        SourcePos(file->getSourceFile(), -1).error("No XML data generated when parsing");
+    }
+    close(fd);
+    return state.root;
+}
+
+XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace)
+    : mNextAttributeIndex(0x80000000)
+    , mFilename(filename)
+    , mStartLineNumber(0)
+    , mEndLineNumber(0)
+{
+    if (isNamespace) {
+        mNamespacePrefix = s1;
+        mNamespaceUri = s2;
+    } else {
+        mNamespaceUri = s1;
+        mElementName = s2;
+    }
+}
+
+XMLNode::XMLNode(const String8& filename)
+    : mFilename(filename)
+{
+}
+
+XMLNode::type XMLNode::getType() const
+{
+    if (mElementName.size() != 0) {
+        return TYPE_ELEMENT;
+    }
+    if (mNamespaceUri.size() != 0) {
+        return TYPE_NAMESPACE;
+    }
+    return TYPE_CDATA;
+}
+
+const String16& XMLNode::getNamespacePrefix() const
+{
+    return mNamespacePrefix;
+}
+
+const String16& XMLNode::getNamespaceUri() const
+{
+    return mNamespaceUri;
+}
+
+const String16& XMLNode::getElementNamespace() const
+{
+    return mNamespaceUri;
+}
+
+const String16& XMLNode::getElementName() const
+{
+    return mElementName;
+}
+
+const Vector<sp<XMLNode> >& XMLNode::getChildren() const
+{
+    return mChildren;
+}
+
+const Vector<XMLNode::attribute_entry>&
+    XMLNode::getAttributes() const
+{
+    return mAttributes;
+}
+
+const String16& XMLNode::getCData() const
+{
+    return mChars;
+}
+
+const String16& XMLNode::getComment() const
+{
+    return mComment;
+}
+
+int32_t XMLNode::getStartLineNumber() const
+{
+    return mStartLineNumber;
+}
+
+int32_t XMLNode::getEndLineNumber() const
+{
+    return mEndLineNumber;
+}
+
+status_t XMLNode::addChild(const sp<XMLNode>& child)
+{
+    if (getType() == TYPE_CDATA) {
+        SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node.");
+        return UNKNOWN_ERROR;
+    }
+    //printf("Adding child %p to parent %p\n", child.get(), this);
+    mChildren.add(child);
+    return NO_ERROR;
+}
+
+status_t XMLNode::addAttribute(const String16& ns, const String16& name,
+                               const String16& value)
+{
+    if (getType() == TYPE_CDATA) {
+        SourcePos(mFilename, getStartLineNumber()).error("Child to CDATA node.");
+        return UNKNOWN_ERROR;
+    }
+    attribute_entry e;
+    e.index = mNextAttributeIndex++;
+    e.ns = ns;
+    e.name = name;
+    e.string = value;
+    mAttributes.add(e);
+    mAttributeOrder.add(e.index, mAttributes.size()-1);
+    return NO_ERROR;
+}
+
+void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId)
+{
+    attribute_entry& e = mAttributes.editItemAt(attrIdx);
+    if (e.nameResId) {
+        mAttributeOrder.removeItem(e.nameResId);
+    } else {
+        mAttributeOrder.removeItem(e.index);
+    }
+    NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n",
+            String8(getElementName()).string(),
+            String8(mAttributes.itemAt(attrIdx).name).string(),
+            String8(mAttributes.itemAt(attrIdx).string).string(),
+            resId));
+    mAttributes.editItemAt(attrIdx).nameResId = resId;
+    mAttributeOrder.add(resId, attrIdx);
+}
+
+status_t XMLNode::appendChars(const String16& chars)
+{
+    if (getType() != TYPE_CDATA) {
+        SourcePos(mFilename, getStartLineNumber()).error("Adding characters to element node.");
+        return UNKNOWN_ERROR;
+    }
+    mChars.append(chars);
+    return NO_ERROR;
+}
+
+status_t XMLNode::appendComment(const String16& comment)
+{
+    if (mComment.size() > 0) {
+        mComment.append(String16("\n"));
+    }
+    mComment.append(comment);
+    return NO_ERROR;
+}
+
+void XMLNode::setStartLineNumber(int32_t line)
+{
+    mStartLineNumber = line;
+}
+
+void XMLNode::setEndLineNumber(int32_t line)
+{
+    mEndLineNumber = line;
+}
+
+void XMLNode::removeWhitespace(bool stripAll, const char** cDataTags)
+{
+    //printf("Removing whitespace in %s\n", String8(mElementName).string());
+    size_t N = mChildren.size();
+    if (cDataTags) {
+        String8 tag(mElementName);
+        const char** p = cDataTags;
+        while (*p) {
+            if (tag == *p) {
+                stripAll = false;
+                break;
+            }
+        }
+    }
+    for (size_t i=0; i<N; i++) {
+        sp<XMLNode> node = mChildren.itemAt(i);
+        if (node->getType() == TYPE_CDATA) {
+            // This is a CDATA node...
+            const char16_t* p = node->mChars.string();
+            while (*p != 0 && *p < 128 && isspace(*p)) {
+                p++;
+            }
+            //printf("Space ends at %d in \"%s\"\n",
+            //       (int)(p-node->mChars.string()),
+            //       String8(node->mChars).string());
+            if (*p == 0) {
+                if (stripAll) {
+                    // Remove this node!
+                    mChildren.removeAt(i);
+                    N--;
+                    i--;
+                } else {
+                    node->mChars = String16(" ");
+                }
+            } else {
+                // Compact leading/trailing whitespace.
+                const char16_t* e = node->mChars.string()+node->mChars.size()-1;
+                while (e > p && *e < 128 && isspace(*e)) {
+                    e--;
+                }
+                if (p > node->mChars.string()) {
+                    p--;
+                }
+                if (e < (node->mChars.string()+node->mChars.size()-1)) {
+                    e++;
+                }
+                if (p > node->mChars.string() ||
+                    e < (node->mChars.string()+node->mChars.size()-1)) {
+                    String16 tmp(p, e-p+1);
+                    node->mChars = tmp;
+                }
+            }
+        } else {
+            node->removeWhitespace(stripAll, cDataTags);
+        }
+    }
+}
+
+status_t XMLNode::parseValues(const sp<AaptAssets>& assets,
+                              ResourceTable* table)
+{
+    bool hasErrors = false;
+    
+    if (getType() == TYPE_ELEMENT) {
+        const size_t N = mAttributes.size();
+        String16 defPackage(assets->getPackage());
+        for (size_t i=0; i<N; i++) {
+            attribute_entry& e = mAttributes.editItemAt(i);
+            AccessorCookie ac(SourcePos(mFilename, getStartLineNumber()), String8(e.name),
+                    String8(e.string));
+            table->setCurrentXmlPos(SourcePos(mFilename, getStartLineNumber()));
+            if (!assets->getIncludedResources()
+                    .stringToValue(&e.value, &e.string,
+                                  e.string.string(), e.string.size(), true, true,
+                                  e.nameResId, NULL, &defPackage, table, &ac)) {
+                hasErrors = true;
+            }
+            NOISY(printf("Attr %s: type=0x%x, str=%s\n",
+                   String8(e.name).string(), e.value.dataType,
+                   String8(e.string).string()));
+        }
+    }
+    const size_t N = mChildren.size();
+    for (size_t i=0; i<N; i++) {
+        status_t err = mChildren.itemAt(i)->parseValues(assets, table);
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets,
+                                    const ResourceTable* table)
+{
+    bool hasErrors = false;
+    
+    if (getType() == TYPE_ELEMENT) {
+        String16 attr("attr");
+        const char* errorMsg;
+        const size_t N = mAttributes.size();
+        for (size_t i=0; i<N; i++) {
+            const attribute_entry& e = mAttributes.itemAt(i);
+            if (e.ns.size() <= 0) continue;
+            bool nsIsPublic;
+            String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic));
+            NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n",
+                    String8(getElementName()).string(),
+                    String8(e.name).string(),
+                    String8(e.string).string(),
+                    String8(e.ns).string(),
+                    (nsIsPublic) ? "public" : "private",
+                    String8(pkg).string()));
+            if (pkg.size() <= 0) continue;
+            uint32_t res = table != NULL
+                ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic)
+                : assets->getIncludedResources().
+                    identifierForName(e.name.string(), e.name.size(),
+                                      attr.string(), attr.size(),
+                                      pkg.string(), pkg.size());
+            if (res != 0) {
+                NOISY(printf("XML attribute name %s: resid=0x%08x\n",
+                             String8(e.name).string(), res));
+                setAttributeResID(i, res);
+            } else {
+                SourcePos(mFilename, getStartLineNumber()).error(
+                        "No resource identifier found for attribute '%s' in package '%s'\n",
+                        String8(e.name).string(), String8(pkg).string());
+                hasErrors = true;
+            }
+        }
+    }
+    const size_t N = mChildren.size();
+    for (size_t i=0; i<N; i++) {
+        status_t err = mChildren.itemAt(i)->assignResourceIds(assets, table);
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
+status_t XMLNode::flatten(const sp<AaptFile>& dest,
+        bool stripComments, bool stripRawValues) const
+{
+    StringPool strings;
+    Vector<uint32_t> resids;
+    
+    // First collect just the strings for attribute names that have a
+    // resource ID assigned to them.  This ensures that the resource ID
+    // array is compact, and makes it easier to deal with attribute names
+    // in different namespaces (and thus with different resource IDs).
+    collect_resid_strings(&strings, &resids);
+
+    // Next collect all remainibng strings.
+    collect_strings(&strings, &resids, stripComments, stripRawValues);
+
+#if 0  // No longer compiles
+    NOISY(printf("Found strings:\n");
+        const size_t N = strings.size();
+        for (size_t i=0; i<N; i++) {
+            printf("%s\n", String8(strings.entryAt(i).string).string());
+        }
+    );
+#endif    
+
+    sp<AaptFile> stringPool = strings.createStringBlock();
+    NOISY(aout << "String pool:"
+          << HexDump(stringPool->getData(), stringPool->getSize()) << endl);
+
+    ResXMLTree_header header;
+    memset(&header, 0, sizeof(header));
+    header.header.type = htods(RES_XML_TYPE);
+    header.header.headerSize = htods(sizeof(header));
+
+    const size_t basePos = dest->getSize();
+    dest->writeData(&header, sizeof(header));
+    dest->writeData(stringPool->getData(), stringPool->getSize());
+
+    // If we have resource IDs, write them.
+    if (resids.size() > 0) {
+        const size_t resIdsPos = dest->getSize();
+        const size_t resIdsSize =
+            sizeof(ResChunk_header)+(sizeof(uint32_t)*resids.size());
+        ResChunk_header* idsHeader = (ResChunk_header*)
+            (((const uint8_t*)dest->editData(resIdsPos+resIdsSize))+resIdsPos);
+        idsHeader->type = htods(RES_XML_RESOURCE_MAP_TYPE);
+        idsHeader->headerSize = htods(sizeof(*idsHeader));
+        idsHeader->size = htodl(resIdsSize);
+        uint32_t* ids = (uint32_t*)(idsHeader+1);
+        for (size_t i=0; i<resids.size(); i++) {
+            *ids++ = htodl(resids[i]);
+        }
+    }
+
+    flatten_node(strings, dest, stripComments, stripRawValues);
+
+    void* data = dest->editData();
+    ResXMLTree_header* hd = (ResXMLTree_header*)(((uint8_t*)data)+basePos);
+    size_t size = dest->getSize()-basePos;
+    hd->header.size = htodl(dest->getSize()-basePos);
+
+    NOISY(aout << "XML resource:"
+          << HexDump(dest->getData(), dest->getSize()) << endl);
+
+    #if PRINT_STRING_METRICS
+    fprintf(stderr, "**** total xml size: %d / %d%% strings (in %s)\n",
+        dest->getSize(), (stringPool->getSize()*100)/dest->getSize(),
+        dest->getPath().string());
+    #endif
+        
+    return NO_ERROR;
+}
+
+void XMLNode::print(int indent)
+{
+    String8 prefix;
+    int i;
+    for (i=0; i<indent; i++) {
+        prefix.append("  ");
+    }
+    if (getType() == TYPE_ELEMENT) {
+        String8 elemNs(getNamespaceUri());
+        if (elemNs.size() > 0) {
+            elemNs.append(":");
+        }
+        printf("%s E: %s%s", prefix.string(),
+               elemNs.string(), String8(getElementName()).string());
+        int N = mAttributes.size();
+        for (i=0; i<N; i++) {
+            ssize_t idx = mAttributeOrder.valueAt(i);
+            if (i == 0) {
+                printf(" / ");
+            } else {
+                printf(", ");
+            }
+            const attribute_entry& attr = mAttributes.itemAt(idx);
+            String8 attrNs(attr.ns);
+            if (attrNs.size() > 0) {
+                attrNs.append(":");
+            }
+            if (attr.nameResId) {
+                printf("%s%s(0x%08x)", attrNs.string(),
+                       String8(attr.name).string(), attr.nameResId);
+            } else {
+                printf("%s%s", attrNs.string(), String8(attr.name).string());
+            }
+            printf("=%s", String8(attr.string).string());
+        }
+        printf("\n");
+    } else if (getType() == TYPE_NAMESPACE) {
+        printf("%s N: %s=%s\n", prefix.string(),
+               getNamespacePrefix().size() > 0
+                    ? String8(getNamespacePrefix()).string() : "<DEF>",
+               String8(getNamespaceUri()).string());
+    } else {
+        printf("%s C: \"%s\"\n", prefix.string(), String8(getCData()).string());
+    }
+    int N = mChildren.size();
+    for (i=0; i<N; i++) {
+        mChildren.itemAt(i)->print(indent+1);
+    }
+}
+
+static void splitName(const char* name, String16* outNs, String16* outName)
+{
+    const char* p = name;
+    while (*p != 0 && *p != 1) {
+        p++;
+    }
+    if (*p == 0) {
+        *outNs = String16();
+        *outName = String16(name);
+    } else {
+        *outNs = String16(name, (p-name));
+        *outName = String16(p+1);
+    }
+}
+
+void XMLCALL
+XMLNode::startNamespace(void *userData, const char *prefix, const char *uri)
+{
+    NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix, uri));
+    ParseState* st = (ParseState*)userData;
+    sp<XMLNode> node = XMLNode::newNamespace(st->filename, 
+            String16(prefix != NULL ? prefix : ""), String16(uri));
+    node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
+    if (st->stack.size() > 0) {
+        st->stack.itemAt(st->stack.size()-1)->addChild(node);
+    } else {
+        st->root = node;
+    }
+    st->stack.push(node);
+}
+
+void XMLCALL
+XMLNode::startElement(void *userData, const char *name, const char **atts)
+{
+    NOISY_PARSE(printf("Start Element: %s\n", name));
+    ParseState* st = (ParseState*)userData;
+    String16 ns16, name16;
+    splitName(name, &ns16, &name16);
+    sp<XMLNode> node = XMLNode::newElement(st->filename, ns16, name16);
+    node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
+    if (st->pendingComment.size() > 0) {
+        node->appendComment(st->pendingComment);
+        st->pendingComment = String16();
+    }
+    if (st->stack.size() > 0) {
+        st->stack.itemAt(st->stack.size()-1)->addChild(node);
+    } else {
+        st->root = node;
+    }
+    st->stack.push(node);
+
+    for (int i = 0; atts[i]; i += 2) {
+        splitName(atts[i], &ns16, &name16);
+        node->addAttribute(ns16, name16, String16(atts[i+1]));
+    }
+}
+
+void XMLCALL
+XMLNode::characterData(void *userData, const XML_Char *s, int len)
+{
+    NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s, len).string()));
+    ParseState* st = (ParseState*)userData;
+    sp<XMLNode> node = NULL;
+    if (st->stack.size() == 0) {
+        return;
+    }
+    sp<XMLNode> parent = st->stack.itemAt(st->stack.size()-1);
+    if (parent != NULL && parent->getChildren().size() > 0) {
+        node = parent->getChildren()[parent->getChildren().size()-1];
+        if (node->getType() != TYPE_CDATA) {
+            // Last node is not CDATA, need to make a new node.
+            node = NULL;
+        }
+    }
+
+    if (node == NULL) {
+        node = XMLNode::newCData(st->filename);
+        node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
+        parent->addChild(node);
+    }
+
+    node->appendChars(String16(s, len));
+}
+
+void XMLCALL
+XMLNode::endElement(void *userData, const char *name)
+{
+    NOISY_PARSE(printf("End Element: %s\n", name));
+    ParseState* st = (ParseState*)userData;
+    sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1);
+    node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser));
+    if (st->pendingComment.size() > 0) {
+        node->appendComment(st->pendingComment);
+        st->pendingComment = String16();
+    }
+    String16 ns16, name16;
+    splitName(name, &ns16, &name16);
+    LOG_ALWAYS_FATAL_IF(node->getElementNamespace() != ns16
+                        || node->getElementName() != name16,
+                        "Bad end element %s", name);
+    st->stack.pop();
+}
+
+void XMLCALL
+XMLNode::endNamespace(void *userData, const char *prefix)
+{
+    const char* nonNullPrefix = prefix != NULL ? prefix : "";
+    NOISY_PARSE(printf("End Namespace: %s\n", prefix));
+    ParseState* st = (ParseState*)userData;
+    sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1);
+    node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser));
+    LOG_ALWAYS_FATAL_IF(node->getNamespacePrefix() != String16(nonNullPrefix),
+                        "Bad end namespace %s", prefix);
+    st->stack.pop();
+}
+
+void XMLCALL
+XMLNode::commentData(void *userData, const char *comment)
+{
+    NOISY_PARSE(printf("Comment: %s\n", comment));
+    ParseState* st = (ParseState*)userData;
+    if (st->pendingComment.size() > 0) {
+        st->pendingComment.append(String16("\n"));
+    }
+    st->pendingComment.append(String16(comment));
+}
+
+status_t XMLNode::collect_strings(StringPool* dest, Vector<uint32_t>* outResIds,
+        bool stripComments, bool stripRawValues) const
+{
+    collect_attr_strings(dest, outResIds, true);
+    
+    int i;
+    if (mNamespacePrefix.size() > 0) {
+        dest->add(mNamespacePrefix, true);
+    }
+    if (mNamespaceUri.size() > 0) {
+        dest->add(mNamespaceUri, true);
+    }
+    if (mElementName.size() > 0) {
+        dest->add(mElementName, true);
+    }
+
+    if (!stripComments && mComment.size() > 0) {
+        dest->add(mComment, true);
+    }
+
+    const int NA = mAttributes.size();
+
+    for (i=0; i<NA; i++) {
+        const attribute_entry& ae = mAttributes.itemAt(i);
+        if (ae.ns.size() > 0) {
+            dest->add(ae.ns, true);
+        }
+        if (!stripRawValues || ae.needStringValue()) {
+            dest->add(ae.string, true);
+        }
+        /*
+        if (ae.value.dataType == Res_value::TYPE_NULL
+                || ae.value.dataType == Res_value::TYPE_STRING) {
+            dest->add(ae.string, true);
+        }
+        */
+    }
+
+    if (mElementName.size() == 0) {
+        // If not an element, include the CDATA, even if it is empty.
+        dest->add(mChars, true);
+    }
+
+    const int NC = mChildren.size();
+
+    for (i=0; i<NC; i++) {
+        mChildren.itemAt(i)->collect_strings(dest, outResIds,
+                stripComments, stripRawValues);
+    }
+
+    return NO_ERROR;
+}
+
+status_t XMLNode::collect_attr_strings(StringPool* outPool,
+        Vector<uint32_t>* outResIds, bool allAttrs) const {
+    const int NA = mAttributes.size();
+
+    for (int i=0; i<NA; i++) {
+        const attribute_entry& attr = mAttributes.itemAt(i);
+        uint32_t id = attr.nameResId;
+        if (id || allAttrs) {
+            // See if we have already assigned this resource ID to a pooled
+            // string...
+            const Vector<size_t>* indices = outPool->offsetsForString(attr.name);
+            ssize_t idx = -1;
+            if (indices != NULL) {
+                const int NJ = indices->size();
+                const size_t NR = outResIds->size();
+                for (int j=0; j<NJ; j++) {
+                    size_t strIdx = indices->itemAt(j);
+                    if (strIdx >= NR) {
+                        if (id == 0) {
+                            // We don't need to assign a resource ID for this one.
+                            idx = strIdx;
+                            break;
+                        }
+                        // Just ignore strings that are out of range of
+                        // the currently assigned resource IDs...  we add
+                        // strings as we assign the first ID.
+                    } else if (outResIds->itemAt(strIdx) == id) {
+                        idx = strIdx;
+                        break;
+                    }
+                }
+            }
+            if (idx < 0) {
+                idx = outPool->add(attr.name);
+                NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n",
+                        String8(attr.name).string(), id, idx));
+                if (id != 0) {
+                    while ((ssize_t)outResIds->size() <= idx) {
+                        outResIds->add(0);
+                    }
+                    outResIds->replaceAt(id, idx);
+                }
+            }
+            attr.namePoolIdx = idx;
+            NOISY(printf("String %s offset=0x%08x\n",
+                         String8(attr.name).string(), idx));
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t XMLNode::collect_resid_strings(StringPool* outPool,
+        Vector<uint32_t>* outResIds) const
+{
+    collect_attr_strings(outPool, outResIds, false);
+
+    const int NC = mChildren.size();
+
+    for (int i=0; i<NC; i++) {
+        mChildren.itemAt(i)->collect_resid_strings(outPool, outResIds);
+    }
+
+    return NO_ERROR;
+}
+
+status_t XMLNode::flatten_node(const StringPool& strings, const sp<AaptFile>& dest,
+        bool stripComments, bool stripRawValues) const
+{
+    ResXMLTree_node node;
+    ResXMLTree_cdataExt cdataExt;
+    ResXMLTree_namespaceExt namespaceExt;
+    ResXMLTree_attrExt attrExt;
+    const void* extData = NULL;
+    size_t extSize = 0;
+    ResXMLTree_attribute attr;
+
+    const size_t NA = mAttributes.size();
+    const size_t NC = mChildren.size();
+    size_t i;
+
+    LOG_ALWAYS_FATAL_IF(NA != mAttributeOrder.size(), "Attributes messed up!");
+
+    const String16 id16("id");
+    const String16 class16("class");
+    const String16 style16("style");
+
+    const type type = getType();
+    
+    memset(&node, 0, sizeof(node));
+    memset(&attr, 0, sizeof(attr));
+    node.header.headerSize = htods(sizeof(node));
+    node.lineNumber = htodl(getStartLineNumber());
+    if (!stripComments) {
+        node.comment.index = htodl(
+            mComment.size() > 0 ? strings.offsetForString(mComment) : -1);
+        //if (mComment.size() > 0) {
+        //  printf("Flattening comment: %s\n", String8(mComment).string());
+        //}
+    } else {
+        node.comment.index = htodl((uint32_t)-1);
+    }
+    if (type == TYPE_ELEMENT) {
+        node.header.type = htods(RES_XML_START_ELEMENT_TYPE);
+        extData = &attrExt;
+        extSize = sizeof(attrExt);
+        memset(&attrExt, 0, sizeof(attrExt));
+        if (mNamespaceUri.size() > 0) {
+            attrExt.ns.index = htodl(strings.offsetForString(mNamespaceUri));
+        } else {
+            attrExt.ns.index = htodl((uint32_t)-1);
+        }
+        attrExt.name.index = htodl(strings.offsetForString(mElementName));
+        attrExt.attributeStart = htods(sizeof(attrExt));
+        attrExt.attributeSize = htods(sizeof(attr));
+        attrExt.attributeCount = htods(NA);
+        attrExt.idIndex = htods(0);
+        attrExt.classIndex = htods(0);
+        attrExt.styleIndex = htods(0);
+        for (i=0; i<NA; i++) {
+            ssize_t idx = mAttributeOrder.valueAt(i);
+            const attribute_entry& ae = mAttributes.itemAt(idx);
+            if (ae.ns.size() == 0) {
+                if (ae.name == id16) {
+                    attrExt.idIndex = htods(i+1);
+                } else if (ae.name == class16) {
+                    attrExt.classIndex = htods(i+1);
+                } else if (ae.name == style16) {
+                    attrExt.styleIndex = htods(i+1);
+                }
+            }
+        }
+    } else if (type == TYPE_NAMESPACE) {
+        node.header.type = htods(RES_XML_START_NAMESPACE_TYPE);
+        extData = &namespaceExt;
+        extSize = sizeof(namespaceExt);
+        memset(&namespaceExt, 0, sizeof(namespaceExt));
+        if (mNamespacePrefix.size() > 0) {
+            namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
+        } else {
+            namespaceExt.prefix.index = htodl((uint32_t)-1);
+        }
+        namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
+        namespaceExt.uri.index = htodl(strings.offsetForString(mNamespaceUri));
+        LOG_ALWAYS_FATAL_IF(NA != 0, "Namespace nodes can't have attributes!");
+    } else if (type == TYPE_CDATA) {
+        node.header.type = htods(RES_XML_CDATA_TYPE);
+        extData = &cdataExt;
+        extSize = sizeof(cdataExt);
+        memset(&cdataExt, 0, sizeof(cdataExt));
+        cdataExt.data.index = htodl(strings.offsetForString(mChars));
+        cdataExt.typedData.size = htods(sizeof(cdataExt.typedData));
+        cdataExt.typedData.res0 = 0;
+        cdataExt.typedData.dataType = mCharsValue.dataType;
+        cdataExt.typedData.data = htodl(mCharsValue.data);
+        LOG_ALWAYS_FATAL_IF(NA != 0, "CDATA nodes can't have attributes!");
+    }
+
+    node.header.size = htodl(sizeof(node) + extSize + (sizeof(attr)*NA));
+
+    dest->writeData(&node, sizeof(node));
+    if (extSize > 0) {
+        dest->writeData(extData, extSize);
+    }
+
+    for (i=0; i<NA; i++) {
+        ssize_t idx = mAttributeOrder.valueAt(i);
+        const attribute_entry& ae = mAttributes.itemAt(idx);
+        if (ae.ns.size() > 0) {
+            attr.ns.index = htodl(strings.offsetForString(ae.ns));
+        } else {
+            attr.ns.index = htodl((uint32_t)-1);
+        }
+        attr.name.index = htodl(ae.namePoolIdx);
+
+        if (!stripRawValues || ae.needStringValue()) {
+            attr.rawValue.index = htodl(strings.offsetForString(ae.string));
+        } else {
+            attr.rawValue.index = htodl((uint32_t)-1);
+        }
+        attr.typedValue.size = htods(sizeof(attr.typedValue));
+        if (ae.value.dataType == Res_value::TYPE_NULL
+                || ae.value.dataType == Res_value::TYPE_STRING) {
+            attr.typedValue.res0 = 0;
+            attr.typedValue.dataType = Res_value::TYPE_STRING;
+            attr.typedValue.data = htodl(strings.offsetForString(ae.string));
+        } else {
+            attr.typedValue.res0 = 0;
+            attr.typedValue.dataType = ae.value.dataType;
+            attr.typedValue.data = htodl(ae.value.data);
+        }
+        dest->writeData(&attr, sizeof(attr));
+    }
+
+    for (i=0; i<NC; i++) {
+        status_t err = mChildren.itemAt(i)->flatten_node(strings, dest,
+                stripComments, stripRawValues);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    if (type == TYPE_ELEMENT) {
+        ResXMLTree_endElementExt endElementExt;
+        memset(&endElementExt, 0, sizeof(endElementExt));
+        node.header.type = htods(RES_XML_END_ELEMENT_TYPE);
+        node.header.size = htodl(sizeof(node)+sizeof(endElementExt));
+        node.lineNumber = htodl(getEndLineNumber());
+        node.comment.index = htodl((uint32_t)-1);
+        endElementExt.ns.index = attrExt.ns.index;
+        endElementExt.name.index = attrExt.name.index;
+        dest->writeData(&node, sizeof(node));
+        dest->writeData(&endElementExt, sizeof(endElementExt));
+    } else if (type == TYPE_NAMESPACE) {
+        node.header.type = htods(RES_XML_END_NAMESPACE_TYPE);
+        node.lineNumber = htodl(getEndLineNumber());
+        node.comment.index = htodl((uint32_t)-1);
+        node.header.size = htodl(sizeof(node)+extSize);
+        dest->writeData(&node, sizeof(node));
+        dest->writeData(extData, extSize);
+    }
+
+    return NO_ERROR;
+}
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
new file mode 100644
index 0000000..86548a2
--- /dev/null
+++ b/tools/aapt/XMLNode.h
@@ -0,0 +1,184 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef XML_NODE_H
+#define XML_NODE_H
+
+#include "StringPool.h"
+#include "ResourceTable.h"
+
+class XMLNode;
+
+extern const char* const RESOURCES_ROOT_NAMESPACE;
+extern const char* const RESOURCES_ANDROID_NAMESPACE;
+
+bool isWhitespace(const char16_t* str);
+
+String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic = NULL);
+
+status_t parseStyledString(Bundle* bundle,
+                           const char* fileName,
+                           ResXMLTree* inXml,
+                           const String16& endTag,
+                           String16* outString,
+                           Vector<StringPool::entry_style_span>* outSpans,
+                           bool isPseudolocalizable);
+
+void printXMLBlock(ResXMLTree* block);
+
+status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree,
+                          bool stripAll=true, bool keepComments=false,
+                          const char** cDataTags=NULL);
+
+class XMLNode : public RefBase
+{
+public:
+    static sp<XMLNode> parse(const sp<AaptFile>& file);
+
+    static inline
+    sp<XMLNode> newNamespace(const String8& filename, const String16& prefix, const String16& uri) {
+        return new XMLNode(filename, prefix, uri, true);
+    }
+    
+    static inline
+    sp<XMLNode> newElement(const String8& filename, const String16& ns, const String16& name) {
+        return new XMLNode(filename, ns, name, false);
+    }
+    
+    static inline
+    sp<XMLNode> newCData(const String8& filename) {
+        return new XMLNode(filename);
+    }
+    
+    enum type {
+        TYPE_NAMESPACE,
+        TYPE_ELEMENT,
+        TYPE_CDATA
+    };
+    
+    type getType() const;
+    
+    const String16& getNamespacePrefix() const;
+    const String16& getNamespaceUri() const;
+    
+    const String16& getElementNamespace() const;
+    const String16& getElementName() const;
+    const Vector<sp<XMLNode> >& getChildren() const;
+
+    struct attribute_entry {
+        attribute_entry() : index(~(uint32_t)0), nameResId(0)
+        {
+            value.dataType = Res_value::TYPE_NULL;
+        }
+
+        bool needStringValue() const {
+            return nameResId == 0
+                || value.dataType == Res_value::TYPE_NULL
+                || value.dataType == Res_value::TYPE_STRING;
+        }
+        
+        String16 ns;
+        String16 name;
+        String16 string;
+        Res_value value;
+        uint32_t index;
+        uint32_t nameResId;
+        mutable uint32_t namePoolIdx;
+    };
+
+    const Vector<attribute_entry>& getAttributes() const;
+
+    const String16& getCData() const;
+
+    const String16& getComment() const;
+
+    int32_t getStartLineNumber() const;
+    int32_t getEndLineNumber() const;
+
+    status_t addChild(const sp<XMLNode>& child);
+
+    status_t addAttribute(const String16& ns, const String16& name,
+                          const String16& value);
+
+    void setAttributeResID(size_t attrIdx, uint32_t resId);
+
+    status_t appendChars(const String16& chars);
+
+    status_t appendComment(const String16& comment);
+
+    void setStartLineNumber(int32_t line);
+    void setEndLineNumber(int32_t line);
+
+    void removeWhitespace(bool stripAll=true, const char** cDataTags=NULL);
+
+    status_t parseValues(const sp<AaptAssets>& assets, ResourceTable* table);
+
+    status_t assignResourceIds(const sp<AaptAssets>& assets,
+                               const ResourceTable* table = NULL);
+
+    status_t flatten(const sp<AaptFile>& dest, bool stripComments,
+            bool stripRawValues) const;
+
+    void print(int indent=0);
+
+private:
+    struct ParseState
+    {
+        String8 filename;
+        XML_Parser parser;
+        sp<XMLNode> root;
+        Vector<sp<XMLNode> > stack;
+        String16 pendingComment;
+    };
+
+    static void XMLCALL
+    startNamespace(void *userData, const char *prefix, const char *uri);
+    static void XMLCALL
+    startElement(void *userData, const char *name, const char **atts);
+    static void XMLCALL
+    characterData(void *userData, const XML_Char *s, int len);
+    static void XMLCALL
+    endElement(void *userData, const char *name);
+    static void XMLCALL
+    endNamespace(void *userData, const char *prefix);
+    
+    static void XMLCALL
+    commentData(void *userData, const char *comment);
+    
+    // Creating an element node.
+    XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace);
+    
+    // Creating a CDATA node.
+    XMLNode(const String8& filename);
+    
+    status_t collect_strings(StringPool* dest, Vector<uint32_t>* outResIds,
+            bool stripComments, bool stripRawValues) const;
+
+    status_t collect_attr_strings(StringPool* outPool,
+        Vector<uint32_t>* outResIds, bool allAttrs) const;
+        
+    status_t collect_resid_strings(StringPool* outPool,
+            Vector<uint32_t>* outResIds) const;
+
+    status_t flatten_node(const StringPool& strings, const sp<AaptFile>& dest,
+            bool stripComments, bool stripRawValues) const;
+
+    String16 mNamespacePrefix;
+    String16 mNamespaceUri;
+    String16 mElementName;
+    Vector<sp<XMLNode> > mChildren;
+    Vector<attribute_entry> mAttributes;
+    KeyedVector<uint32_t, uint32_t> mAttributeOrder;
+    uint32_t mNextAttributeIndex;
+    String16 mChars;
+    Res_value mCharsValue;
+    String16 mComment;
+    String8 mFilename;
+    int32_t mStartLineNumber;
+    int32_t mEndLineNumber;
+};
+
+#endif
diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp
new file mode 100644
index 0000000..4cf73d8
--- /dev/null
+++ b/tools/aapt/printapk.cpp
@@ -0,0 +1,127 @@
+#include <utils/ResourceTypes.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <zipfile/zipfile.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+using namespace android;
+
+static int
+usage()
+{
+    fprintf(stderr,
+            "usage: apk APKFILE\n"
+            "\n"
+            "APKFILE   an android packge file produced by aapt.\n"
+            );
+    return 1;
+}
+
+
+int
+main(int argc, char** argv)
+{
+    const char* filename;
+    int fd;
+    ssize_t amt;
+    off_t size;
+    void* buf;
+    zipfile_t zip;
+    zipentry_t entry;
+    void* cookie;
+    void* resfile;
+    int bufsize;
+    int err;
+
+    if (argc != 2) {
+        return usage();
+    }
+
+    filename = argv[1];
+    fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "apk: couldn't open file for read: %s\n", filename);
+        return 1;
+    }
+
+    size = lseek(fd, 0, SEEK_END);
+    amt = lseek(fd, 0, SEEK_SET);
+
+    if (size < 0 || amt < 0) {
+        fprintf(stderr, "apk: error determining file size: %s\n", filename);
+        return 1;
+    }
+
+    buf = malloc(size);
+    if (buf == NULL) {
+        fprintf(stderr, "apk: file too big: %s\n", filename);
+        return 1;
+    }
+
+    amt = read(fd, buf, size);
+    if (amt != size) {
+        fprintf(stderr, "apk: error reading file: %s\n", filename);
+        return 1;
+    }
+
+    close(fd);
+
+    zip = init_zipfile(buf, size);
+    if (zip == NULL) {
+        fprintf(stderr, "apk: file doesn't seem to be a zip file: %s\n",
+                filename);
+        return 1;
+    }
+
+    printf("files:\n");
+    cookie = NULL;
+    while ((entry = iterate_zipfile(zip, &cookie))) {
+        char* name = get_zipentry_name(entry);
+        printf("  %s\n", name);
+        free(name);
+    }
+
+    entry = lookup_zipentry(zip, "resources.arsc");
+    if (entry != NULL) {
+        size = get_zipentry_size(entry);
+        bufsize = size + (size / 1000) + 1;
+        resfile = malloc(bufsize);
+
+        err = decompress_zipentry(entry, resfile, bufsize);
+        if (err != 0) {
+            fprintf(stderr, "apk: error decompressing resources.arsc");
+            return 1;
+        }
+
+        ResTable res(resfile, size, resfile);
+        res.print();
+#if 0
+        size_t tableCount = res.getTableCount();
+        printf("Tables: %d\n", (int)tableCount);
+        for (size_t tableIndex=0; tableIndex<tableCount; tableIndex++) {
+            const ResStringPool* strings = res.getTableStringBlock(tableIndex);
+            size_t stringCount = strings->size();
+            for (size_t stringIndex=0; stringIndex<stringCount; stringIndex++) {
+                size_t len;
+                const char16_t* ch = strings->stringAt(stringIndex, &len);
+                String8 s(String16(ch, len));
+                printf("  [%3d] %s\n", (int)stringIndex, s.string());
+            }
+        }
+
+        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));
+            printf("  [%3d] %s\n", (int)bpIndex, s.string());
+        }
+#endif
+    }
+
+
+    return 0;
+}
diff --git a/tools/aapt/tests/plurals/AndroidManifest.xml b/tools/aapt/tests/plurals/AndroidManifest.xml
new file mode 100644
index 0000000..c721dee
--- /dev/null
+++ b/tools/aapt/tests/plurals/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.aapt.test.plurals">
+
+</manifest>
diff --git a/tools/aapt/tests/plurals/res/values/strings.xml b/tools/aapt/tests/plurals/res/values/strings.xml
new file mode 100644
index 0000000..1c1fc19
--- /dev/null
+++ b/tools/aapt/tests/plurals/res/values/strings.xml
@@ -0,0 +1,7 @@
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="ok">OK</string>
+    <plurals name="a_plural">
+        <item quantity="one">A dog</item>
+        <item quantity="other">Some dogs</item>
+    </plurals>
+</resources>
diff --git a/tools/aapt/tests/plurals/run.sh b/tools/aapt/tests/plurals/run.sh
new file mode 100755
index 0000000..4d39e10
--- /dev/null
+++ b/tools/aapt/tests/plurals/run.sh
@@ -0,0 +1,16 @@
+TEST_DIR=tools/aapt/tests/plurals
+TEST_OUT_DIR=out/plurals_test
+
+rm -rf $TEST_OUT_DIR
+mkdir -p $TEST_OUT_DIR
+mkdir -p $TEST_OUT_DIR/java
+
+#gdb --args \
+aapt package -v -x -m -z  -J $TEST_OUT_DIR/java -M $TEST_DIR/AndroidManifest.xml \
+             -I out/target/common/obj/APPS/framework-res_intermediates/package-export.apk \
+             -P $TEST_OUT_DIR/public_resources.xml \
+             -S $TEST_DIR/res
+
+echo
+echo "==================== FILES CREATED ==================== "
+find $TEST_OUT_DIR -type f
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp
new file mode 100755
index 0000000..91802a9
--- /dev/null
+++ b/tools/aidl/AST.cpp
@@ -0,0 +1,867 @@
+#include "AST.h"
+#include "Type.h"
+
+void
+WriteModifiers(FILE* to, int mod, int mask)
+{
+    int m = mod & mask;
+
+    if ((m & SCOPE_MASK) == PUBLIC) {
+        fprintf(to, "public ");
+    }
+    else if ((m & SCOPE_MASK) == PRIVATE) {
+        fprintf(to, "private ");
+    }
+    else if ((m & SCOPE_MASK) == PROTECTED) {
+        fprintf(to, "protected ");
+    }
+
+    if (m & STATIC) {
+        fprintf(to, "static ");
+    }
+    
+    if (m & FINAL) {
+        fprintf(to, "final ");
+    }
+
+    if (m & ABSTRACT) {
+        fprintf(to, "abstract ");
+    }
+}
+
+void
+WriteArgumentList(FILE* to, const vector<Expression*>& arguments)
+{
+    size_t N = arguments.size();
+    for (size_t i=0; i<N; i++) {
+        arguments[i]->Write(to);
+        if (i != N-1) {
+            fprintf(to, ", ");
+        }
+    }
+}
+
+ClassElement::ClassElement()
+{
+}
+
+ClassElement::~ClassElement()
+{
+}
+
+Field::Field()
+    :ClassElement(),
+     modifiers(0),
+     variable(NULL)
+{
+}
+
+Field::Field(int m, Variable* v)
+    :ClassElement(),
+     modifiers(m),
+     variable(v)
+{
+}
+
+Field::~Field()
+{
+}
+
+void
+Field::GatherTypes(set<Type*>* types) const
+{
+    types->insert(this->variable->type);
+}
+
+void
+Field::Write(FILE* to)
+{
+    if (this->comment.length() != 0) {
+        fprintf(to, "%s\n", this->comment.c_str());
+    }
+    WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL);
+    fprintf(to, "%s %s", this->variable->type->QualifiedName().c_str(),
+            this->variable->name.c_str());
+    if (this->value.length() != 0) {
+        fprintf(to, " = %s", this->value.c_str());
+    }
+    fprintf(to, ";\n");
+}
+
+Expression::~Expression()
+{
+}
+
+LiteralExpression::LiteralExpression(const string& v)
+    :value(v)
+{
+}
+
+LiteralExpression::~LiteralExpression()
+{
+}
+
+void
+LiteralExpression::Write(FILE* to)
+{
+    fprintf(to, "%s", this->value.c_str());
+}
+
+Variable::Variable()
+    :type(NULL),
+     name(),
+     dimension(0)
+{
+}
+
+Variable::Variable(Type* t, const string& n)
+    :type(t),
+     name(n),
+     dimension(0)
+{
+}
+
+Variable::Variable(Type* t, const string& n, int d)
+    :type(t),
+     name(n),
+     dimension(d)
+{
+}
+
+Variable::~Variable()
+{
+}
+
+void
+Variable::GatherTypes(set<Type*>* types) const
+{
+    types->insert(this->type);
+}
+
+void
+Variable::WriteDeclaration(FILE* to)
+{
+    string dim;
+    for (int i=0; i<this->dimension; i++) {
+        dim += "[]";
+    }
+    fprintf(to, "%s%s %s", this->type->QualifiedName().c_str(), dim.c_str(),
+            this->name.c_str());
+}
+
+void
+Variable::Write(FILE* to)
+{
+    fprintf(to, "%s", name.c_str());
+}
+
+FieldVariable::FieldVariable(Expression* o, const string& n)
+    :object(o),
+     clazz(NULL),
+     name(n)
+{
+}
+
+FieldVariable::FieldVariable(Type* c, const string& n)
+    :object(NULL),
+     clazz(c),
+     name(n)
+{
+}
+
+FieldVariable::~FieldVariable()
+{
+}
+
+void
+FieldVariable::Write(FILE* to)
+{
+    if (this->object != NULL) {
+        this->object->Write(to);
+    }
+    else if (this->clazz != NULL) {
+        fprintf(to, "%s", this->clazz->QualifiedName().c_str());
+    }
+    fprintf(to, ".%s", name.c_str());
+}
+
+
+Statement::~Statement()
+{
+}
+
+StatementBlock::StatementBlock()
+{
+}
+
+StatementBlock::~StatementBlock()
+{
+}
+
+void
+StatementBlock::Write(FILE* to)
+{
+    fprintf(to, "{\n");
+    int N = this->statements.size();
+    for (int i=0; i<N; i++) {
+        this->statements[i]->Write(to);
+    }
+    fprintf(to, "}\n");
+}
+
+void
+StatementBlock::Add(Statement* statement)
+{
+    this->statements.push_back(statement);
+}
+
+void
+StatementBlock::Add(Expression* expression)
+{
+    this->statements.push_back(new ExpressionStatement(expression));
+}
+
+ExpressionStatement::ExpressionStatement(Expression* e)
+    :expression(e)
+{
+}
+
+ExpressionStatement::~ExpressionStatement()
+{
+}
+
+void
+ExpressionStatement::Write(FILE* to)
+{
+    this->expression->Write(to);
+    fprintf(to, ";\n");
+}
+
+Assignment::Assignment(Variable* l, Expression* r)
+    :lvalue(l),
+     rvalue(r),
+     cast(NULL)
+{
+}
+
+Assignment::Assignment(Variable* l, Expression* r, Type* c)
+    :lvalue(l),
+     rvalue(r),
+     cast(c)
+{
+}
+
+Assignment::~Assignment()
+{
+}
+
+void
+Assignment::Write(FILE* to)
+{
+    this->lvalue->Write(to);
+    fprintf(to, " = ");
+    if (this->cast != NULL) {
+        fprintf(to, "(%s)", this->cast->QualifiedName().c_str());
+    }
+    this->rvalue->Write(to);
+}
+
+MethodCall::MethodCall(const string& n)
+    :obj(NULL),
+     clazz(NULL),
+     name(n)
+{
+}
+
+MethodCall::MethodCall(Expression* o, const string& n)
+    :obj(o),
+     clazz(NULL),
+     name(n)
+{
+}
+
+MethodCall::MethodCall(Type* t, const string& n)
+    :obj(NULL),
+     clazz(t),
+     name(n)
+{
+}
+
+MethodCall::MethodCall(Expression* o, const string& n, int argc = 0, ...)
+    :obj(o),
+     clazz(NULL),
+     name(n)
+{
+  va_list args;
+  va_start(args, argc);
+  init(argc, args);
+  va_end(args);
+}
+
+MethodCall::MethodCall(Type* t, const string& n, int argc = 0, ...)
+    :obj(NULL),
+     clazz(t),
+     name(n)
+{
+  va_list args;
+  va_start(args, argc);
+  init(argc, args);
+  va_end(args);
+}
+
+MethodCall::~MethodCall()
+{
+}
+
+void
+MethodCall::init(int n, va_list args)
+{
+    for (int i=0; i<n; i++) {
+        Expression* expression = (Expression*)va_arg(args, void*);
+        this->arguments.push_back(expression);
+    }
+}
+
+void
+MethodCall::Write(FILE* to)
+{
+    if (this->obj != NULL) {
+        this->obj->Write(to);
+        fprintf(to, ".");
+    }
+    else if (this->clazz != NULL) {
+        fprintf(to, "%s.", this->clazz->QualifiedName().c_str());
+    }
+    fprintf(to, "%s(", this->name.c_str());
+    WriteArgumentList(to, this->arguments);
+    fprintf(to, ")");
+}
+
+Comparison::Comparison(Expression* l, const string& o, Expression* r)
+    :lvalue(l),
+     op(o),
+     rvalue(r)
+{
+}
+
+Comparison::~Comparison()
+{
+}
+
+void
+Comparison::Write(FILE* to)
+{
+    fprintf(to, "(");
+    this->lvalue->Write(to);
+    fprintf(to, "%s", this->op.c_str());
+    this->rvalue->Write(to);
+    fprintf(to, ")");
+}
+
+NewExpression::NewExpression(Type* t)
+    :type(t)
+{
+}
+
+NewExpression::~NewExpression()
+{
+}
+
+void
+NewExpression::Write(FILE* to)
+{
+    fprintf(to, "new %s(", this->type->InstantiableName().c_str());
+    WriteArgumentList(to, this->arguments);
+    fprintf(to, ")");
+}
+
+NewArrayExpression::NewArrayExpression(Type* t, Expression* s)
+    :type(t),
+     size(s)
+{
+}
+
+NewArrayExpression::~NewArrayExpression()
+{
+}
+
+void
+NewArrayExpression::Write(FILE* to)
+{
+    fprintf(to, "new %s[", this->type->QualifiedName().c_str());
+    size->Write(to);
+    fprintf(to, "]");
+}
+
+Ternary::Ternary()
+    :condition(NULL),
+     ifpart(NULL),
+     elsepart(NULL)
+{
+}
+
+Ternary::Ternary(Expression* a, Expression* b, Expression* c)
+    :condition(a),
+     ifpart(b),
+     elsepart(c)
+{
+}
+
+Ternary::~Ternary()
+{
+}
+
+void
+Ternary::Write(FILE* to)
+{
+    fprintf(to, "((");
+    this->condition->Write(to);
+    fprintf(to, ")?(");
+    this->ifpart->Write(to);
+    fprintf(to, "):(");
+    this->elsepart->Write(to);
+    fprintf(to, "))");
+}
+
+Cast::Cast()
+    :type(NULL),
+     expression(NULL)
+{
+}
+
+Cast::Cast(Type* t, Expression* e)
+    :type(t),
+     expression(e)
+{
+}
+
+Cast::~Cast()
+{
+}
+
+void
+Cast::Write(FILE* to)
+{
+    fprintf(to, "((%s)", this->type->QualifiedName().c_str());
+    expression->Write(to);
+    fprintf(to, ")");
+}
+
+VariableDeclaration::VariableDeclaration(Variable* l, Expression* r, Type* c)
+    :lvalue(l),
+     cast(c),
+     rvalue(r)
+{
+}
+
+VariableDeclaration::VariableDeclaration(Variable* l)
+    :lvalue(l),
+     cast(NULL),
+     rvalue(NULL)
+{
+}
+
+VariableDeclaration::~VariableDeclaration()
+{
+}
+
+void
+VariableDeclaration::Write(FILE* to)
+{
+    this->lvalue->WriteDeclaration(to);
+    if (this->rvalue != NULL) {
+        fprintf(to, " = ");
+        if (this->cast != NULL) {
+            fprintf(to, "(%s)", this->cast->QualifiedName().c_str());
+        }
+        this->rvalue->Write(to);
+    }
+    fprintf(to, ";\n");
+}
+
+IfStatement::IfStatement()
+    :expression(NULL),
+     statements(new StatementBlock),
+     elseif(NULL)
+{
+}
+
+IfStatement::~IfStatement()
+{
+}
+
+void
+IfStatement::Write(FILE* to)
+{
+    if (this->expression != NULL) {
+        fprintf(to, "if (");
+        this->expression->Write(to);
+        fprintf(to, ") ");
+    }
+    this->statements->Write(to);
+    if (this->elseif != NULL) {
+        fprintf(to, "else ");
+        this->elseif->Write(to);
+    }
+}
+
+ReturnStatement::ReturnStatement(Expression* e)
+    :expression(e)
+{
+}
+
+ReturnStatement::~ReturnStatement()
+{
+}
+
+void
+ReturnStatement::Write(FILE* to)
+{
+    fprintf(to, "return ");
+    this->expression->Write(to);
+    fprintf(to, ";\n");
+}
+
+TryStatement::TryStatement()
+    :statements(new StatementBlock)
+{
+}
+
+TryStatement::~TryStatement()
+{
+}
+
+void
+TryStatement::Write(FILE* to)
+{
+    fprintf(to, "try ");
+    this->statements->Write(to);
+}
+
+CatchStatement::CatchStatement(Variable* e)
+    :statements(new StatementBlock),
+     exception(e)
+{
+}
+
+CatchStatement::~CatchStatement()
+{
+}
+
+void
+CatchStatement::Write(FILE* to)
+{
+    fprintf(to, "catch ");
+    if (this->exception != NULL) {
+        fprintf(to, "(");
+        this->exception->WriteDeclaration(to);
+        fprintf(to, ") ");
+    }
+    this->statements->Write(to);
+}
+
+FinallyStatement::FinallyStatement()
+    :statements(new StatementBlock)
+{
+}
+
+FinallyStatement::~FinallyStatement()
+{
+}
+
+void
+FinallyStatement::Write(FILE* to)
+{
+    fprintf(to, "finally ");
+    this->statements->Write(to);
+}
+
+Case::Case()
+    :statements(new StatementBlock)
+{
+}
+
+Case::Case(const string& c)
+    :statements(new StatementBlock)
+{
+    cases.push_back(c);
+}
+
+Case::~Case()
+{
+}
+
+void
+Case::Write(FILE* to)
+{
+    int N = this->cases.size();
+    if (N > 0) {
+        for (int i=0; i<N; i++) {
+            string s = this->cases[i];
+            if (s.length() != 0) {
+                fprintf(to, "case %s:\n", s.c_str());
+            } else {
+                fprintf(to, "default:\n");
+            }
+        }
+    } else {
+        fprintf(to, "default:\n");
+    }
+    statements->Write(to);
+}
+
+SwitchStatement::SwitchStatement(Expression* e)
+    :expression(e)
+{
+}
+
+SwitchStatement::~SwitchStatement()
+{
+}
+
+void
+SwitchStatement::Write(FILE* to)
+{
+    fprintf(to, "switch (");
+    this->expression->Write(to);
+    fprintf(to, ")\n{\n");
+    int N = this->cases.size();
+    for (int i=0; i<N; i++) {
+        this->cases[i]->Write(to);
+    }
+    fprintf(to, "}\n");
+}
+
+Method::Method()
+    :ClassElement(),
+     modifiers(0),
+     returnType(NULL), // (NULL means constructor)
+     returnTypeDimension(0),
+     statements(NULL)
+{
+}
+
+Method::~Method()
+{
+}
+
+void
+Method::GatherTypes(set<Type*>* types) const
+{
+    size_t N, i;
+
+    if (this->returnType) {
+        types->insert(this->returnType);
+    }
+
+    N = this->parameters.size();
+    for (i=0; i<N; i++) {
+        this->parameters[i]->GatherTypes(types);
+    }
+
+    N = this->exceptions.size();
+    for (i=0; i<N; i++) {
+        types->insert(this->exceptions[i]);
+    }
+}
+
+void
+Method::Write(FILE* to)
+{
+    size_t N, i;
+
+    if (this->comment.length() != 0) {
+        fprintf(to, "%s\n", this->comment.c_str());
+    }
+
+    WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL);
+
+    if (this->returnType != NULL) {
+        string dim;
+        for (i=0; i<this->returnTypeDimension; i++) {
+            dim += "[]";
+        }
+        fprintf(to, "%s%s ", this->returnType->QualifiedName().c_str(),
+                dim.c_str());
+    }
+   
+    fprintf(to, "%s(", this->name.c_str());
+
+    N = this->parameters.size();
+    for (i=0; i<N; i++) {
+        this->parameters[i]->WriteDeclaration(to);
+        if (i != N-1) {
+            fprintf(to, ", ");
+        }
+    }
+
+    fprintf(to, ")");
+
+    N = this->exceptions.size();
+    for (i=0; i<N; i++) {
+        if (i == 0) {
+            fprintf(to, " throws ");
+        } else {
+            fprintf(to, ", ");
+        }
+        fprintf(to, "%s", this->exceptions[i]->QualifiedName().c_str());
+    }
+
+    if (this->statements == NULL) {
+        fprintf(to, ";\n");
+    } else {
+        fprintf(to, "\n");
+        this->statements->Write(to);
+    }
+}
+
+Class::Class()
+    :modifiers(0),
+     what(CLASS),
+     type(NULL),
+     extends(NULL)
+{
+}
+
+Class::~Class()
+{
+}
+
+void
+Class::GatherTypes(set<Type*>* types) const
+{
+    int N, i;
+
+    types->insert(this->type);
+    if (this->extends != NULL) {
+        types->insert(this->extends);
+    }
+
+    N = this->interfaces.size();
+    for (i=0; i<N; i++) {
+        types->insert(this->interfaces[i]);
+    }
+
+    N = this->elements.size();
+    for (i=0; i<N; i++) {
+        this->elements[i]->GatherTypes(types);
+    }
+}
+
+void
+Class::Write(FILE* to)
+{
+    size_t N, i;
+
+    if (this->comment.length() != 0) {
+        fprintf(to, "%s\n", this->comment.c_str());
+    }
+
+    WriteModifiers(to, this->modifiers, ALL_MODIFIERS);
+
+    if (this->what == Class::CLASS) {
+        fprintf(to, "class ");
+    } else {
+        fprintf(to, "interface ");
+    }
+
+    string name = this->type->Name();
+    size_t pos = name.rfind('.');
+    if (pos != string::npos) {
+        name = name.c_str() + pos + 1;
+    }
+
+    fprintf(to, "%s", name.c_str());
+
+    if (this->extends != NULL) {
+        fprintf(to, " extends %s", this->extends->QualifiedName().c_str());
+    }
+
+    N = this->interfaces.size();
+    if (N != 0) {
+        if (this->what == Class::CLASS) {
+            fprintf(to, " implements");
+        } else {
+            fprintf(to, " extends");
+        }
+        for (i=0; i<N; i++) {
+            fprintf(to, " %s", this->interfaces[i]->QualifiedName().c_str());
+        }
+    }
+
+    fprintf(to, "\n");
+    fprintf(to, "{\n");
+
+    N = this->elements.size();
+    for (i=0; i<N; i++) {
+        this->elements[i]->Write(to);
+    }
+
+    fprintf(to, "}\n");
+
+}
+
+Document::Document()
+{
+}
+
+Document::~Document()
+{
+}
+
+static string
+escape_backslashes(const string& str)
+{
+    string result;
+    const size_t I=str.length();
+    for (size_t i=0; i<I; i++) {
+        char c = str[i];
+        if (c == '\\') {
+            result += "\\\\";
+        } else {
+            result += c;
+        }
+    }
+    return result;
+}
+
+void
+Document::Write(FILE* to)
+{
+    size_t N, i;
+
+    if (this->comment.length() != 0) {
+        fprintf(to, "%s\n", this->comment.c_str());
+    }
+    fprintf(to, "/*\n"
+                " * This file is auto-generated.  DO NOT MODIFY.\n"
+                " * Original file: %s\n"
+                " */\n", escape_backslashes(this->originalSrc).c_str());
+    if (this->package.length() != 0) {
+        fprintf(to, "package %s;\n", this->package.c_str());
+    }
+
+    // gather the types for the import statements
+    set<Type*> types;
+    N = this->classes.size();
+    for (i=0; i<N; i++) {
+        Class* c = this->classes[i];
+        c->GatherTypes(&types);
+    }
+    
+    set<Type*>::iterator it;
+    for (it=types.begin(); it!=types.end(); it++) {
+        Type* t = *it;
+        string pkg = t->Package();
+        if (pkg.length() != 0 && pkg != this->package) {
+            fprintf(to, "import %s;\n", t->ImportType().c_str());
+        }
+    }
+
+    N = this->classes.size();
+    for (i=0; i<N; i++) {
+        Class* c = this->classes[i];
+        c->Write(to);
+    }
+}
+
diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h
new file mode 100755
index 0000000..1dedd04
--- /dev/null
+++ b/tools/aidl/AST.h
@@ -0,0 +1,346 @@
+#ifndef AIDL_AST_H
+#define AIDL_AST_H
+
+#include <string>
+#include <vector>
+#include <set>
+#include <stdarg.h>
+
+using namespace std;
+
+class Type;
+
+enum {
+    PACKAGE_PRIVATE = 0x00000000,
+    PUBLIC          = 0x00000001,
+    PRIVATE         = 0x00000002,
+    PROTECTED       = 0x00000003,
+    SCOPE_MASK      = 0x00000003,
+
+    STATIC          = 0x00000010,
+    FINAL           = 0x00000020,
+    ABSTRACT        = 0x00000040,
+
+    ALL_MODIFIERS   = 0xffffffff
+};
+
+// Write the modifiers that are set in both mod and mask
+void WriteModifiers(FILE* to, int mod, int mask);
+
+struct ClassElement
+{
+    ClassElement();
+    virtual ~ClassElement();
+
+    virtual void GatherTypes(set<Type*>* types) const = 0;
+    virtual void Write(FILE* to) = 0;
+};
+
+struct Expression
+{
+    virtual ~Expression();
+    virtual void Write(FILE* to) = 0;
+};
+
+struct LiteralExpression : public Expression
+{
+    string value;
+
+    LiteralExpression(const string& value);
+    virtual ~LiteralExpression();
+    virtual void Write(FILE* to);
+};
+
+struct Variable : public Expression
+{
+    Type* type;
+    string name;
+    int dimension;
+
+    Variable();
+    Variable(Type* type, const string& name);
+    Variable(Type* type, const string& name, int dimension);
+    virtual ~Variable();
+
+    virtual void GatherTypes(set<Type*>* types) const;
+    void WriteDeclaration(FILE* to);
+    void Write(FILE* to);
+};
+
+struct FieldVariable : public Expression
+{
+    Expression* object;
+    Type* clazz;
+    string name;
+
+    FieldVariable(Expression* object, const string& name);
+    FieldVariable(Type* clazz, const string& name);
+    virtual ~FieldVariable();
+
+    void Write(FILE* to);
+};
+
+struct Field : public ClassElement
+{
+    string comment;
+    int modifiers;
+    Variable *variable;
+    string value;
+
+    Field();
+    Field(int modifiers, Variable* variable);
+    virtual ~Field();
+
+    virtual void GatherTypes(set<Type*>* types) const;
+    virtual void Write(FILE* to);
+};
+
+struct Statement
+{
+    virtual ~Statement();
+    virtual void Write(FILE* to) = 0;
+};
+
+struct StatementBlock
+{
+    vector<Statement*> statements;
+
+    StatementBlock();
+    virtual ~StatementBlock();
+    virtual void Write(FILE* to);
+
+    void Add(Statement* statement);
+    void Add(Expression* expression);
+};
+
+struct ExpressionStatement : public Statement
+{
+    Expression* expression;
+
+    ExpressionStatement(Expression* expression);
+    virtual ~ExpressionStatement();
+    virtual void Write(FILE* to);
+};
+
+struct Assignment : public Expression
+{
+    Variable* lvalue;
+    Expression* rvalue;
+    Type* cast;
+
+    Assignment(Variable* lvalue, Expression* rvalue);
+    Assignment(Variable* lvalue, Expression* rvalue, Type* cast);
+    virtual ~Assignment();
+    virtual void Write(FILE* to);
+};
+
+struct MethodCall : public Expression
+{
+    Expression* obj;
+    Type* clazz;
+    string name;
+    vector<Expression*> arguments;
+    vector<string> exceptions;
+
+    MethodCall(const string& name);
+    MethodCall(Expression* obj, const string& name);
+    MethodCall(Type* clazz, const string& name);
+    MethodCall(Expression* obj, const string& name, int argc, ...);
+    MethodCall(Type* clazz, const string& name, int argc, ...);
+    virtual ~MethodCall();
+    virtual void Write(FILE* to);
+
+private:
+    void init(int n, va_list args);
+};
+
+struct Comparison : public Expression
+{
+    Expression* lvalue;
+    string op;
+    Expression* rvalue;
+
+    Comparison(Expression* lvalue, const string& op, Expression* rvalue);
+    virtual ~Comparison();
+    virtual void Write(FILE* to);
+};
+
+struct NewExpression : public Expression
+{
+    Type* type;
+    vector<Expression*> arguments;
+
+    NewExpression(Type* type);
+    virtual ~NewExpression();
+    virtual void Write(FILE* to);
+};
+
+struct NewArrayExpression : public Expression
+{
+    Type* type;
+    Expression* size;
+
+    NewArrayExpression(Type* type, Expression* size);
+    virtual ~NewArrayExpression();
+    virtual void Write(FILE* to);
+};
+
+struct Ternary : public Expression
+{
+    Expression* condition;
+    Expression* ifpart;
+    Expression* elsepart;
+
+    Ternary();
+    Ternary(Expression* condition, Expression* ifpart, Expression* elsepart);
+    virtual ~Ternary();
+    virtual void Write(FILE* to);
+};
+
+struct Cast : public Expression
+{
+    Type* type;
+    Expression* expression;
+
+    Cast();
+    Cast(Type* type, Expression* expression);
+    virtual ~Cast();
+    virtual void Write(FILE* to);
+};
+
+struct VariableDeclaration : public Statement
+{
+    Variable* lvalue;
+    Type* cast;
+    Expression* rvalue;
+
+    VariableDeclaration(Variable* lvalue);
+    VariableDeclaration(Variable* lvalue, Expression* rvalue, Type* cast = NULL);
+    virtual ~VariableDeclaration();
+    virtual void Write(FILE* to);
+};
+
+struct IfStatement : public Statement
+{
+    Expression* expression;
+    StatementBlock* statements;
+    IfStatement* elseif;
+
+    IfStatement();
+    virtual ~IfStatement();
+    virtual void Write(FILE* to);
+};
+
+struct ReturnStatement : public Statement
+{
+    Expression* expression;
+
+    ReturnStatement(Expression* expression);
+    virtual ~ReturnStatement();
+    virtual void Write(FILE* to);
+};
+
+struct TryStatement : public Statement
+{
+    StatementBlock* statements;
+
+    TryStatement();
+    virtual ~TryStatement();
+    virtual void Write(FILE* to);
+};
+
+struct CatchStatement : public Statement
+{
+    StatementBlock* statements;
+    Variable* exception;
+
+    CatchStatement(Variable* exception);
+    virtual ~CatchStatement();
+    virtual void Write(FILE* to);
+};
+
+struct FinallyStatement : public Statement
+{
+    StatementBlock* statements;
+
+    FinallyStatement();
+    virtual ~FinallyStatement();
+    virtual void Write(FILE* to);
+};
+
+struct Case
+{
+    vector<string> cases;
+    StatementBlock* statements;
+
+    Case();
+    Case(const string& c);
+    virtual ~Case();
+    virtual void Write(FILE* to);
+};
+
+struct SwitchStatement : public Statement
+{
+    Expression* expression;
+    vector<Case*> cases;
+
+    SwitchStatement(Expression* expression);
+    virtual ~SwitchStatement();
+    virtual void Write(FILE* to);
+};
+
+struct Method : public ClassElement
+{
+    string comment;
+    int modifiers;
+    Type* returnType;
+    size_t returnTypeDimension;
+    string name;
+    vector<Variable*> parameters;
+    vector<Type*> exceptions;
+    StatementBlock* statements;
+
+    Method();
+    virtual ~Method();
+
+    virtual void GatherTypes(set<Type*>* types) const;
+    virtual void Write(FILE* to);
+};
+
+struct Class : public ClassElement
+{
+    enum {
+        CLASS,
+        INTERFACE
+    };
+
+    string comment;
+    int modifiers;
+    int what;               // CLASS or INTERFACE
+    Type* type;
+    Type* extends;
+    vector<Type*> interfaces;
+    vector<ClassElement*> elements;
+
+    Class();
+    virtual ~Class();
+
+    virtual void GatherTypes(set<Type*>* types) const;
+    virtual void Write(FILE* to);
+};
+
+struct Document
+{
+    string comment;
+    string package;
+    string originalSrc;
+    set<Type*> imports;
+    vector<Class*> classes;
+
+    Document();
+    virtual ~Document();
+
+    virtual void Write(FILE* to);
+};
+
+#endif // AIDL_AST_H
diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk
new file mode 100644
index 0000000..944aeb6
--- /dev/null
+++ b/tools/aidl/Android.mk
@@ -0,0 +1,24 @@
+# Copyright 2007 The Android Open Source Project
+#
+# Copies files into the directory structure described by a manifest
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	aidl_language_l.l \
+	aidl_language_y.y \
+	aidl.cpp \
+	aidl_language.cpp \
+	options.cpp \
+	search_path.cpp \
+	AST.cpp \
+	Type.cpp \
+	generate_java.cpp
+
+LOCAL_CFLAGS := -g
+LOCAL_MODULE := aidl
+
+include $(BUILD_HOST_EXECUTABLE)
+
+
diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp
new file mode 100755
index 0000000..a44072d
--- /dev/null
+++ b/tools/aidl/Type.cpp
@@ -0,0 +1,1228 @@
+#include "Type.h"
+
+Namespace NAMES;
+
+Type* VOID_TYPE;
+Type* BOOLEAN_TYPE;
+Type* BYTE_TYPE;
+Type* CHAR_TYPE;
+Type* INT_TYPE;
+Type* LONG_TYPE;
+Type* FLOAT_TYPE;
+Type* DOUBLE_TYPE;
+Type* STRING_TYPE;
+Type* CHAR_SEQUENCE_TYPE;
+Type* TEXT_UTILS_TYPE;
+Type* REMOTE_EXCEPTION_TYPE;
+Type* RUNTIME_EXCEPTION_TYPE;
+Type* IBINDER_TYPE;
+Type* IINTERFACE_TYPE;
+Type* BINDER_NATIVE_TYPE;
+Type* BINDER_PROXY_TYPE;
+Type* PARCEL_TYPE;
+Type* PARCELABLE_INTERFACE_TYPE;
+Type* MAP_TYPE;
+Type* LIST_TYPE;
+Type* CLASSLOADER_TYPE;
+
+Expression* NULL_VALUE;
+Expression* THIS_VALUE;
+Expression* SUPER_VALUE;
+Expression* TRUE_VALUE;
+Expression* FALSE_VALUE;
+
+void
+register_base_types()
+{
+    VOID_TYPE = new BasicType("void", "XXX", "XXX", "XXX", "XXX", "XXX");
+    NAMES.Add(VOID_TYPE);
+
+    BOOLEAN_TYPE = new BooleanType();
+    NAMES.Add(BOOLEAN_TYPE);
+
+    BYTE_TYPE = new BasicType("byte", "writeByte", "readByte",
+                "writeByteArray", "createByteArray", "readByteArray");
+    NAMES.Add(BYTE_TYPE);
+
+    CHAR_TYPE = new CharType();
+    NAMES.Add(CHAR_TYPE);
+
+    INT_TYPE = new BasicType("int", "writeInt", "readInt",
+                "writeIntArray", "createIntArray", "readIntArray");
+    NAMES.Add(INT_TYPE);
+
+    LONG_TYPE = new BasicType("long", "writeLong", "readLong",
+                "writeLongArray", "createLongArray", "readLongArray");
+    NAMES.Add(LONG_TYPE);
+
+    FLOAT_TYPE = new BasicType("float", "writeFloat", "readFloat",
+                "writeFloatArray", "createFloatArray", "readFloatArray");
+    NAMES.Add(FLOAT_TYPE);
+
+    DOUBLE_TYPE = new BasicType("double", "writeDouble", "readDouble",
+                "writeDoubleArray", "createDoubleArray", "readDoubleArray");
+    NAMES.Add(DOUBLE_TYPE);
+
+    STRING_TYPE = new StringType();
+    NAMES.Add(STRING_TYPE);
+
+    CHAR_SEQUENCE_TYPE = new CharSequenceType();
+    NAMES.Add(CHAR_SEQUENCE_TYPE);
+
+    MAP_TYPE = new MapType();
+    NAMES.Add(MAP_TYPE);
+
+    LIST_TYPE = new ListType();
+    NAMES.Add(LIST_TYPE);
+
+    TEXT_UTILS_TYPE = new Type("android.text", "TextUtils",
+                                    Type::BUILT_IN, false, false);
+    NAMES.Add(TEXT_UTILS_TYPE);
+
+    REMOTE_EXCEPTION_TYPE = new RemoteExceptionType();
+    NAMES.Add(REMOTE_EXCEPTION_TYPE);
+
+    RUNTIME_EXCEPTION_TYPE = new RuntimeExceptionType();
+    NAMES.Add(RUNTIME_EXCEPTION_TYPE);
+
+    IBINDER_TYPE = new IBinderType();
+    NAMES.Add(IBINDER_TYPE);
+
+    IINTERFACE_TYPE = new IInterfaceType();
+    NAMES.Add(IINTERFACE_TYPE);
+
+    BINDER_NATIVE_TYPE = new BinderType();
+    NAMES.Add(BINDER_NATIVE_TYPE);
+
+    BINDER_PROXY_TYPE = new BinderProxyType();
+    NAMES.Add(BINDER_PROXY_TYPE);
+
+    PARCEL_TYPE = new ParcelType();
+    NAMES.Add(PARCEL_TYPE);
+
+    PARCELABLE_INTERFACE_TYPE = new ParcelableInterfaceType();
+    NAMES.Add(PARCELABLE_INTERFACE_TYPE);
+
+    CLASSLOADER_TYPE = new ClassLoaderType();
+    NAMES.Add(CLASSLOADER_TYPE);
+
+    NULL_VALUE = new LiteralExpression("null");
+    THIS_VALUE = new LiteralExpression("this");
+    SUPER_VALUE = new LiteralExpression("super");
+    TRUE_VALUE = new LiteralExpression("true");
+    FALSE_VALUE = new LiteralExpression("false");
+
+    NAMES.AddGenericType("java.util", "List", 1);
+    NAMES.AddGenericType("java.util", "Map", 2);
+}
+
+static Type*
+make_generic_type(const string& package, const string& name,
+                    const vector<Type*>& args)
+{
+    if (package == "java.util" && name == "List") {
+        return new GenericListType("java.util", "List", args);
+    }
+    return NULL;
+    //return new GenericType(package, name, args);
+}
+
+// ================================================================
+
+Type::Type(const string& name, int kind, bool canWriteToParcel, bool canBeOut)
+    :m_package(),
+     m_name(name),
+     m_declFile(""),
+     m_declLine(-1),
+     m_kind(kind),
+     m_canWriteToParcel(canWriteToParcel),
+     m_canBeOut(canBeOut)
+{
+    m_qualifiedName = name;
+}
+
+Type::Type(const string& package, const string& name,
+            int kind, bool canWriteToParcel, bool canBeOut,
+            const string& declFile, int declLine)
+    :m_package(package),
+     m_name(name),
+     m_declFile(declFile),
+     m_declLine(declLine),
+     m_kind(kind),
+     m_canWriteToParcel(canWriteToParcel),
+     m_canBeOut(canBeOut)
+{
+    if (package.length() > 0) {
+        m_qualifiedName = package;
+        m_qualifiedName += '.';
+    }
+    m_qualifiedName += name;
+}
+
+Type::~Type()
+{
+}
+
+bool
+Type::CanBeArray() const
+{
+    return false;
+}
+
+string
+Type::ImportType() const
+{
+    return m_qualifiedName;
+}
+
+string
+Type::CreatorName() const
+{
+    return "";
+}
+
+string
+Type::InstantiableName() const
+{
+    return QualifiedName();
+}
+
+
+void
+Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%sn",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* WriteToParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* CreateFromParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* ReadFromParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* WriteArrayToParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* CreateArrayFromParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* ReadArrayFromParcel error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::SetQualifiedName(const string& qualified)
+{
+    m_qualifiedName = qualified;
+}
+
+Expression*
+Type::BuildWriteToParcelFlags(int flags)
+{
+    if (flags == 0) {
+        return new LiteralExpression("0");
+    }
+    if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+        return new FieldVariable(PARCELABLE_INTERFACE_TYPE,
+                "PARCELABLE_WRITE_RETURN_VALUE");
+    }
+    return new LiteralExpression("0");
+}
+
+// ================================================================
+
+BasicType::BasicType(const string& name, const string& marshallMethod,
+                     const string& unmarshallMethod,
+                     const string& writeArray, const string& createArray,
+                     const string& readArray)
+    :Type(name, BUILT_IN, true, false),
+     m_marshallMethod(marshallMethod),
+     m_unmarshallMethod(unmarshallMethod),
+     m_writeArrayMethod(writeArray),
+     m_createArrayMethod(createArray),
+     m_readArrayMethod(readArray)
+{
+}
+
+void
+BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, m_marshallMethod, 1, v));
+}
+
+void
+BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallMethod)));
+}
+
+bool
+BasicType::CanBeArray() const
+{
+    return true;
+}
+
+void
+BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, m_writeArrayMethod, 1, v));
+}
+
+void
+BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayMethod)));
+}
+
+void
+BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new MethodCall(parcel, m_readArrayMethod, 1, v));
+}
+
+
+// ================================================================
+
+BooleanType::BooleanType()
+    :Type("boolean", BUILT_IN, true, false)
+{
+}
+
+void
+BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeInt", 1, 
+                new Ternary(v, new LiteralExpression("1"),
+                    new LiteralExpression("0"))));
+}
+
+void
+BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"),
+                    "!=", new MethodCall(parcel, "readInt"))));
+}
+
+bool
+BooleanType::CanBeArray() const
+{
+    return true;
+}
+
+void
+BooleanType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeBooleanArray", 1, v));
+}
+
+void
+BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray")));
+}
+
+void
+BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
+}
+
+
+// ================================================================
+
+CharType::CharType()
+    :Type("char", BUILT_IN, true, false)
+{
+}
+
+void
+CharType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeInt", 1, 
+                    new Cast(INT_TYPE, v)));
+}
+
+void
+CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this));
+}
+
+bool
+CharType::CanBeArray() const
+{
+    return true;
+}
+
+void
+CharType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeCharArray", 1, v));
+}
+
+void
+CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray")));
+}
+
+void
+CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
+}
+
+// ================================================================
+
+StringType::StringType()
+    :Type("java.lang", "String", BUILT_IN, true, false)
+{
+}
+
+string
+StringType::CreatorName() const
+{
+    return "android.os.Parcel.STRING_CREATOR";
+}
+
+void
+StringType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeString", 1, v));
+}
+
+void
+StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readString")));
+}
+
+bool
+StringType::CanBeArray() const
+{
+    return true;
+}
+
+void
+StringType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeStringArray", 1, v));
+}
+
+void
+StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray")));
+}
+
+void
+StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
+}
+
+// ================================================================
+
+CharSequenceType::CharSequenceType()
+    :Type("java.lang", "CharSequence", BUILT_IN, true, false)
+{
+}
+
+string
+CharSequenceType::CreatorName() const
+{
+    return "android.os.Parcel.STRING_CREATOR";
+}
+
+void
+CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    // if (v != null) {
+    //     parcel.writeInt(1);
+    //     v.writeToParcel(parcel);
+    // } else {
+    //     parcel.writeInt(0);
+    // }
+    IfStatement* elsepart = new IfStatement();
+    elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
+                                new LiteralExpression("0")));
+    IfStatement* ifpart = new IfStatement;
+    ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
+    ifpart->elseif = elsepart;
+    ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
+                                new LiteralExpression("1")));
+    ifpart->statements->Add(new MethodCall(TEXT_UTILS_TYPE, "writeToParcel",
+                                3, v, parcel, BuildWriteToParcelFlags(flags)));
+
+    addTo->Add(ifpart);
+}
+
+void
+CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                Variable* parcel)
+{
+    // if (0 != parcel.readInt()) {
+    //     v = TextUtils.createFromParcel(parcel)
+    // } else {
+    //     v = null;
+    // }
+    IfStatement* elsepart = new IfStatement();
+    elsepart->statements->Add(new Assignment(v, NULL_VALUE));
+
+    IfStatement* ifpart = new IfStatement();
+    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+                new MethodCall(parcel, "readInt"));
+    ifpart->elseif = elsepart;
+    ifpart->statements->Add(new Assignment(v,
+                new MethodCall(TEXT_UTILS_TYPE,
+                                    "CHAR_SEQUENCE_CREATOR.createFromParcel", 1, parcel)));
+
+    addTo->Add(ifpart);
+}
+
+
+// ================================================================
+
+RemoteExceptionType::RemoteExceptionType()
+    :Type("android.os", "RemoteException", BUILT_IN, false, false)
+{
+}
+
+void
+RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+RuntimeExceptionType::RuntimeExceptionType()
+    :Type("java.lang", "RuntimeException", BUILT_IN, false, false)
+{
+}
+
+void
+RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+
+// ================================================================
+
+IBinderType::IBinderType()
+    :Type("android.os", "IBinder", BUILT_IN, true, false)
+{
+}
+
+void
+IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, v));
+}
+
+void
+IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder")));
+}
+
+void
+IBinderType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeBinderArray", 1, v));
+}
+
+void
+IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray")));
+}
+
+void
+IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v));
+}
+
+
+// ================================================================
+
+IInterfaceType::IInterfaceType()
+    :Type("android.os", "IInterface", BUILT_IN, false, false)
+{
+}
+
+void
+IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+
+// ================================================================
+
+BinderType::BinderType()
+    :Type("android.os", "Binder", BUILT_IN, false, false)
+{
+}
+
+void
+BinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+
+// ================================================================
+
+BinderProxyType::BinderProxyType()
+    :Type("android.os", "BinderProxy", BUILT_IN, false, false)
+{
+}
+
+void
+BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+
+// ================================================================
+
+ParcelType::ParcelType()
+    :Type("android.os", "Parcel", BUILT_IN, false, false)
+{
+}
+
+void
+ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+ParcelableInterfaceType::ParcelableInterfaceType()
+    :Type("android.os", "Parcelable", BUILT_IN, false, false)
+{
+}
+
+void
+ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void
+ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+MapType::MapType()
+    :Type("java.util", "Map", BUILT_IN, true, true)
+{
+}
+
+void
+MapType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeMap", 1, v));
+}
+
+void
+MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
+    addTo->Add(new VariableDeclaration(cl,
+        new LiteralExpression("this.getClass().getClassLoader()"),
+        CLASSLOADER_TYPE));
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, cl)));
+}
+
+void
+MapType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+                    Variable* parcel)
+{
+    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
+    addTo->Add(new VariableDeclaration(cl, 
+        new LiteralExpression("this.getClass().getClassLoader()"),
+        CLASSLOADER_TYPE));
+    addTo->Add(new MethodCall(parcel, "readMap", 2, v, cl));
+}
+
+
+// ================================================================
+
+ListType::ListType()
+    :Type("java.util", "List", BUILT_IN, true, true)
+{
+}
+
+string
+ListType::InstantiableName() const
+{
+    return "java.util.ArrayList";
+}
+
+void
+ListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeList", 1, v));
+}
+
+void
+ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
+    addTo->Add(new VariableDeclaration(cl, 
+        new LiteralExpression("this.getClass().getClassLoader()"),
+        CLASSLOADER_TYPE));
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, cl)));
+}
+
+void
+ListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+                    Variable* parcel)
+{
+    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
+    addTo->Add(new VariableDeclaration(cl, 
+        new LiteralExpression("this.getClass().getClassLoader()"),
+        CLASSLOADER_TYPE));
+    addTo->Add(new MethodCall(parcel, "readList", 2, v, cl));
+}
+
+
+// ================================================================
+
+ParcelableType::ParcelableType(const string& package, const string& name,
+                        bool builtIn, const string& declFile, int declLine)
+    :Type(package, name, builtIn ? BUILT_IN : PARCELABLE, true, true,
+            declFile, declLine)
+{
+}
+
+string
+ParcelableType::CreatorName() const
+{
+    return QualifiedName() + ".CREATOR";
+}
+
+void
+ParcelableType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    // if (v != null) {
+    //     parcel.writeInt(1);
+    //     v.writeToParcel(parcel);
+    // } else {
+    //     parcel.writeInt(0);
+    // }
+    IfStatement* elsepart = new IfStatement();
+    elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1,
+                                new LiteralExpression("0")));
+    IfStatement* ifpart = new IfStatement;
+    ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
+    ifpart->elseif = elsepart;
+    ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1,
+                                new LiteralExpression("1")));
+    ifpart->statements->Add(new MethodCall(v, "writeToParcel", 2,
+                                parcel, BuildWriteToParcelFlags(flags)));
+
+    addTo->Add(ifpart);
+}
+
+void
+ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    // if (0 != parcel.readInt()) {
+    //     v = CLASS.CREATOR.createFromParcel(parcel)
+    // } else {
+    //     v = null;
+    // }
+    IfStatement* elsepart = new IfStatement();
+    elsepart->statements->Add(new Assignment(v, NULL_VALUE));
+
+    IfStatement* ifpart = new IfStatement();
+    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+                new MethodCall(parcel, "readInt"));
+    ifpart->elseif = elsepart;
+    ifpart->statements->Add(new Assignment(v,
+                new MethodCall(v->type, "CREATOR.createFromParcel", 1, parcel)));
+
+    addTo->Add(ifpart);
+}
+
+void
+ParcelableType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+                    Variable* parcel)
+{
+    // TODO: really, we don't need to have this extra check, but we
+    // don't have two separate marshalling code paths
+    // if (0 != parcel.readInt()) {
+    //     v.readFromParcel(parcel)
+    // }
+    IfStatement* ifpart = new IfStatement();
+    ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+                new MethodCall(parcel, "readInt"));
+    ifpart->statements->Add(new MethodCall(v, "readFromParcel", 1, parcel));
+    addTo->Add(ifpart);
+}
+
+bool
+ParcelableType::CanBeArray() const
+{
+    return true;
+}
+
+void
+ParcelableType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v,
+                BuildWriteToParcelFlags(flags)));
+}
+
+void
+ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    string creator = v->type->QualifiedName() + ".CREATOR";
+    addTo->Add(new Assignment(v, new MethodCall(parcel,
+                "createTypedArray", 1, new LiteralExpression(creator))));
+}
+
+void
+ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    string creator = v->type->QualifiedName() + ".CREATOR";
+    addTo->Add(new MethodCall(parcel, "readTypedArray", 2,
+                    v, new LiteralExpression(creator)));
+}
+
+
+// ================================================================
+
+InterfaceType::InterfaceType(const string& package, const string& name,
+                        bool builtIn, bool oneway,
+                        const string& declFile, int declLine)
+    :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false,
+                        declFile, declLine)
+    ,m_oneway(oneway)
+{
+}
+
+bool
+InterfaceType::OneWay() const
+{
+    return m_oneway;
+}
+
+void
+InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    // parcel.writeStrongBinder(v != null ? v.asBinder() : null);
+    addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, 
+                new Ternary(
+                    new Comparison(v, "!=", NULL_VALUE),
+                    new MethodCall(v, "asBinder"),
+                    NULL_VALUE)));
+}
+
+void
+InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    // v = Interface.asInterface(parcel.readStrongBinder());
+    string type = v->type->QualifiedName();
+    type += ".Stub";
+    addTo->Add(new Assignment(v,
+                new MethodCall( NAMES.Find(type), "asInterface", 1,
+                    new MethodCall(parcel, "readStrongBinder"))));
+}
+
+
+// ================================================================
+
+GenericType::GenericType(const string& package, const string& name,
+                         const vector<Type*>& args)
+    :Type(package, name, BUILT_IN, true, true)
+{
+    m_args = args;
+
+    m_importName = package + '.' + name;
+
+    string gen = "<";
+    int N = args.size();
+    for (int i=0; i<N; i++) {
+        Type* t = args[i];
+        gen += t->QualifiedName();
+        if (i != N-1) {
+            gen += ',';
+        }
+    }
+    gen += '>';
+    m_genericArguments = gen;
+    SetQualifiedName(m_importName + gen);
+}
+
+string
+GenericType::GenericArguments() const
+{
+    return m_genericArguments;
+}
+
+string
+GenericType::ImportType() const
+{
+    return m_importName;
+}
+
+void
+GenericType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    fprintf(stderr, "implement GenericType::WriteToParcel\n");
+}
+
+void
+GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    fprintf(stderr, "implement GenericType::CreateFromParcel\n");
+}
+
+void
+GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    fprintf(stderr, "implement GenericType::ReadFromParcel\n");
+}
+
+
+// ================================================================
+
+GenericListType::GenericListType(const string& package, const string& name,
+                         const vector<Type*>& args)
+    :GenericType(package, name, args),
+     m_creator(args[0]->CreatorName())
+{
+}
+
+string
+GenericListType::CreatorName() const
+{
+    return "android.os.Parcel.arrayListCreator";
+}
+
+string
+GenericListType::InstantiableName() const
+{
+    return "java.util.ArrayList" + GenericArguments();
+}
+
+void
+GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+{
+    if (m_creator == STRING_TYPE->CreatorName()) {
+        addTo->Add(new MethodCall(parcel, "writeStringList", 1, v));
+    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
+        addTo->Add(new MethodCall(parcel, "writeBinderList", 1, v));
+    } else {
+        // parcel.writeTypedListXX(arg);
+        addTo->Add(new MethodCall(parcel, "writeTypedList", 1, v));
+    }
+}
+
+void
+GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+{
+    if (m_creator == STRING_TYPE->CreatorName()) {
+        addTo->Add(new Assignment(v,
+                   new MethodCall(parcel, "createStringArrayList", 0)));
+    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
+        addTo->Add(new Assignment(v,
+                   new MethodCall(parcel, "createBinderArrayList", 0)));
+    } else {
+        // v = _data.readTypedArrayList(XXX.creator);
+        addTo->Add(new Assignment(v,
+                   new MethodCall(parcel, "createTypedArrayList", 1,
+                   new LiteralExpression(m_creator))));
+    }
+}
+
+void
+GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    if (m_creator == STRING_TYPE->CreatorName()) {
+        addTo->Add(new MethodCall(parcel, "readStringList", 1, v));
+    } else if (m_creator == IBINDER_TYPE->CreatorName()) {
+        addTo->Add(new MethodCall(parcel, "readBinderList", 1, v));
+    } else {
+        // v = _data.readTypedList(v, XXX.creator);
+        addTo->Add(new MethodCall(parcel, "readTypedList", 2,
+                       v,
+                       new LiteralExpression(m_creator)));
+    }
+}
+
+// ================================================================
+
+ClassLoaderType::ClassLoaderType()
+    :Type("java.lang", "ClassLoader", BUILT_IN, false, false)
+{
+}
+
+
+// ================================================================
+
+Namespace::Namespace()
+{
+}
+
+Namespace::~Namespace()
+{
+    int N = m_types.size();
+    for (int i=0; i<N; i++) {
+        delete m_types[i];
+    }
+}
+
+void
+Namespace::Add(Type* type)
+{
+    Type* t = Find(type->QualifiedName());
+    if (t == NULL) {
+        m_types.push_back(type);
+    }
+}
+
+void
+Namespace::AddGenericType(const string& package, const string& name, int args)
+{
+    Generic g;
+        g.package = package;
+        g.name = name;
+        g.qualified = package + '.' + name;
+        g.args = args;
+    m_generics.push_back(g);
+}
+
+Type*
+Namespace::Find(const string& name) const
+{
+    int N = m_types.size();
+    for (int i=0; i<N; i++) {
+        if (m_types[i]->QualifiedName() == name) {
+            return m_types[i];
+        }
+    }
+    return NULL;
+}
+
+Type*
+Namespace::Find(const char* package, const char* name) const
+{
+    string s;
+    if (package != NULL) {
+        s += package;
+        s += '.';
+    }
+    s += name;
+    return Find(s);
+}
+
+static string
+normalize_generic(const string& s)
+{
+    string r;
+    int N = s.size();
+    for (int i=0; i<N; i++) {
+        char c = s[i];
+        if (!isspace(c)) {
+            r += c;
+        }
+    }
+    return r;
+}
+
+Type*
+Namespace::Search(const string& name)
+{
+    // an exact match wins
+    Type* result = Find(name);
+    if (result != NULL) {
+        return result;
+    }
+
+    // try the class names
+    // our language doesn't allow you to not specify outer classes
+    // when referencing an inner class.  that could be changed, and this
+    // would be the place to do it, but I don't think the complexity in
+    // scoping rules is worth it.
+    int N = m_types.size();
+    for (int i=0; i<N; i++) {
+        if (m_types[i]->Name() == name) {
+            return m_types[i];
+        }
+    }
+
+    // we got to here and it's not a generic, give up
+    if (name.find('<') == name.npos) {
+        return NULL;
+    }
+
+    // remove any whitespace
+    string normalized = normalize_generic(name);
+
+    // find the part before the '<', find a generic for it
+    ssize_t baseIndex = normalized.find('<');
+    string base(normalized.c_str(), baseIndex);
+    const Generic* g = search_generic(base);
+    if (g == NULL) {
+        return NULL;
+    }
+
+    // For each of the args, do a recursive search on it.  We don't allow
+    // generics within generics like Java does, because we're really limiting
+    // them to just built-in container classes, at least for now.  Our syntax
+    // ensures this right now as well.
+    vector<Type*> args;
+    size_t start = baseIndex + 1;
+    size_t end = start;
+    while (normalized[start] != '\0') {
+        end = normalized.find(',', start);
+        if (end == normalized.npos) {
+            end = normalized.find('>', start);
+        }
+        string s(normalized.c_str()+start, end-start);
+        Type* t = this->Search(s);
+        if (t == NULL) {
+            // maybe we should print a warning here?
+            return NULL;
+        }
+        args.push_back(t);
+        start = end+1;
+    }
+
+    // construct a GenericType, add it to our name set so they always get
+    // the same object, and return it.
+    result = make_generic_type(g->package, g->name, args);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    this->Add(result);
+    return this->Find(result->QualifiedName());
+}
+
+const Namespace::Generic*
+Namespace::search_generic(const string& name) const
+{
+    int N = m_generics.size();
+
+    // first exact match
+    for (int i=0; i<N; i++) {
+        const Generic& g = m_generics[i];
+        if (g.qualified == name) {
+            return &g;
+        }
+    }
+
+    // then name match
+    for (int i=0; i<N; i++) {
+        const Generic& g = m_generics[i];
+        if (g.name == name) {
+            return &g;
+        }
+    }
+
+    return NULL;
+}
+
+void
+Namespace::Dump() const
+{
+    int n = m_types.size();
+    for (int i=0; i<n; i++) {
+        Type* t = m_types[i];
+        printf("type: package=%s name=%s qualifiedName=%s\n",
+                t->Package().c_str(), t->Name().c_str(),
+                t->QualifiedName().c_str());
+    }
+}
diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h
new file mode 100755
index 0000000..2ea3ac9
--- /dev/null
+++ b/tools/aidl/Type.h
@@ -0,0 +1,466 @@
+#ifndef AIDL_TYPE_H
+#define AIDL_TYPE_H
+
+#include "AST.h"
+#include <string>
+#include <vector>
+
+using namespace std;
+
+class Type
+{
+public:
+    // kinds
+    enum {
+        BUILT_IN,
+        PARCELABLE,
+        INTERFACE,
+        GENERATED
+    };
+    
+    // WriteToParcel flags
+    enum {
+        PARCELABLE_WRITE_RETURN_VALUE = 0x0001
+    };
+
+                    Type(const string& name, int kind, bool canWriteToParcel,
+                            bool canBeOut);
+                    Type(const string& package, const string& name,
+                            int kind, bool canWriteToParcel, bool canBeOut,
+                            const string& declFile = "", int declLine = -1);
+    virtual         ~Type();
+
+    inline string   Package() const             { return m_package; }
+    inline string   Name() const                { return m_name; }
+    inline string   QualifiedName() const       { return m_qualifiedName; }
+    inline int      Kind() const                { return m_kind; }
+    inline string   DeclFile() const            { return m_declFile; }
+    inline int      DeclLine() const            { return m_declLine; }
+    inline bool     CanBeMarshalled() const     { return m_canWriteToParcel; }
+    inline bool     CanBeOutParameter() const   { return m_canBeOut; }
+    
+    virtual string  ImportType() const;
+    virtual string  CreatorName() const;
+    virtual string  InstantiableName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+protected:
+    void SetQualifiedName(const string& qualified);
+    Expression* BuildWriteToParcelFlags(int flags);
+
+private:
+    Type();
+    Type(const Type&);
+
+    string m_package;
+    string m_name;
+    string m_qualifiedName;
+    string m_declFile;
+    int m_declLine;
+    int m_kind;
+    bool m_canWriteToParcel;
+    bool m_canBeOut;
+};
+
+class BasicType : public Type
+{
+public:
+                    BasicType(const string& name, const string& marshallMethod,
+                              const string& unmarshallMethod,
+                              const string& writeArray,
+                              const string& createArray,
+                              const string& readArray);
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+private:
+    string m_marshallMethod;
+    string m_unmarshallMethod;
+    string m_writeArrayMethod;
+    string m_createArrayMethod;
+    string m_readArrayMethod;
+};
+
+class BooleanType : public Type
+{
+public:
+                    BooleanType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class CharType : public Type
+{
+public:
+                    CharType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+
+class StringType : public Type
+{
+public:
+                    StringType();
+
+    virtual string  CreatorName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class CharSequenceType : public Type
+{
+public:
+                    CharSequenceType();
+
+    virtual string  CreatorName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class RemoteExceptionType : public Type
+{
+public:
+                    RemoteExceptionType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class RuntimeExceptionType : public Type
+{
+public:
+                    RuntimeExceptionType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class IBinderType : public Type
+{
+public:
+                    IBinderType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class IInterfaceType : public Type
+{
+public:
+                    IInterfaceType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class BinderType : public Type
+{
+public:
+                    BinderType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class BinderProxyType : public Type
+{
+public:
+                    BinderProxyType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class ParcelType : public Type
+{
+public:
+                    ParcelType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class ParcelableInterfaceType : public Type
+{
+public:
+                    ParcelableInterfaceType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class MapType : public Type
+{
+public:
+                    MapType();
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class ListType : public Type
+{
+public:
+                    ListType();
+
+    virtual string  InstantiableName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class ParcelableType : public Type
+{
+public:
+                    ParcelableType(const string& package, const string& name,
+                            bool builtIn, const string& declFile, int declLine);
+
+    virtual string  CreatorName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+    virtual bool    CanBeArray() const;
+
+    virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+};
+
+class InterfaceType : public Type
+{
+public:
+                    InterfaceType(const string& package, const string& name,
+                            bool builtIn, bool oneway,
+                            const string& declFile, int declLine);
+
+    bool            OneWay() const;
+    
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+                                    
+private:
+    bool m_oneway;
+};
+
+
+class GenericType : public Type
+{
+public:
+                    GenericType(const string& package, const string& name,
+                                 const vector<Type*>& args);
+
+    string          GenericArguments() const;
+
+    virtual string  ImportType() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+private:
+    string m_genericArguments;
+    string m_importName;
+    vector<Type*> m_args;
+};
+
+
+class GenericListType : public GenericType
+{
+public:
+                    GenericListType(const string& package, const string& name,
+                                 const vector<Type*>& args);
+
+    virtual string  CreatorName() const;
+    virtual string  InstantiableName() const;
+
+    virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel, int flags);
+    virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+    virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
+                                    Variable* parcel);
+
+private:
+    string m_creator;
+};
+
+class ClassLoaderType : public Type
+{
+public:
+                    ClassLoaderType();
+};
+
+class Namespace
+{
+public:
+            Namespace();
+            ~Namespace();
+    void    Add(Type* type);
+
+    // args is the number of template types (what is this called?)
+    void    AddGenericType(const string& package, const string& name, int args);
+
+    // lookup a specific class name
+    Type*   Find(const string& name) const;
+    Type*   Find(const char* package, const char* name) const;
+    
+    // try to search by either a full name or a partial name
+    Type*   Search(const string& name);
+
+    void    Dump() const;
+
+private:
+    struct Generic {
+        string package;
+        string name;
+        string qualified;
+        int args;
+    };
+
+    const Generic* search_generic(const string& name) const;
+
+    vector<Type*> m_types;
+    vector<Generic> m_generics;
+};
+
+extern Namespace NAMES;
+
+extern Type* VOID_TYPE;
+extern Type* BOOLEAN_TYPE;
+extern Type* CHAR_TYPE;
+extern Type* INT_TYPE;
+extern Type* LONG_TYPE;
+extern Type* FLOAT_TYPE;
+extern Type* DOUBLE_TYPE;
+extern Type* STRING_TYPE;
+extern Type* CHAR_SEQUENCE_TYPE;
+extern Type* TEXT_UTILS_TYPE;
+extern Type* REMOTE_EXCEPTION_TYPE;
+extern Type* RUNTIME_EXCEPTION_TYPE;
+extern Type* IBINDER_TYPE;
+extern Type* IINTERFACE_TYPE;
+extern Type* BINDER_NATIVE_TYPE;
+extern Type* BINDER_PROXY_TYPE;
+extern Type* PARCEL_TYPE;
+extern Type* PARCELABLE_INTERFACE_TYPE;
+
+extern Expression* NULL_VALUE;
+extern Expression* THIS_VALUE;
+extern Expression* SUPER_VALUE;
+extern Expression* TRUE_VALUE;
+extern Expression* FALSE_VALUE;
+
+void register_base_types();
+
+#endif // AIDL_TYPE_H
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
new file mode 100644
index 0000000..dc61567
--- /dev/null
+++ b/tools/aidl/aidl.cpp
@@ -0,0 +1,904 @@
+
+#include "aidl_language.h"
+#include "options.h"
+#include "search_path.h"
+#include "Type.h"
+#include "generate_java.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <map>
+
+#ifdef HAVE_MS_C_RUNTIME
+#include <io.h>
+#include <sys/stat.h>
+#endif
+
+#ifndef O_BINARY
+#  define O_BINARY  0
+#endif
+
+using namespace std;
+
+static void
+test_document(document_item_type* d)
+{
+    while (d) {
+        if (d->item_type == INTERFACE_TYPE) {
+            interface_type* c = (interface_type*)d;
+            printf("interface %s %s {\n", c->package, c->name.data);
+            interface_item_type *q = (interface_item_type*)c->interface_items;
+            while (q) {
+                if (q->item_type == METHOD_TYPE) {
+                    method_type *m = (method_type*)q;
+                    printf("  %s %s(", m->type.type.data, m->name.data);
+                    arg_type *p = m->args;
+                    while (p) {
+                        printf("%s %s",p->type.type.data,p->name.data);
+                        if (p->next) printf(", ");
+                        p=p->next;
+                    }
+                    printf(")");
+                    printf(";\n");
+                }
+                q=q->next;
+            }
+            printf("}\n");
+        }
+        else if (d->item_type == PARCELABLE_TYPE) {
+            parcelable_type* b = (parcelable_type*)d;
+            printf("parcelable %s %s;\n", b->package, b->name.data);
+        }
+        else {
+            printf("UNKNOWN d=0x%08x d->item_type=%ld\n", (long)d, d->item_type);
+        }
+        d = d->next;
+    }
+}
+
+// ==========================================================
+int
+convert_direction(const char* direction)
+{
+    if (direction == NULL) {
+        return IN_PARAMETER;
+    }
+    if (0 == strcmp(direction, "in")) {
+        return IN_PARAMETER;
+    }
+    if (0 == strcmp(direction, "out")) {
+        return OUT_PARAMETER;
+    }
+    return INOUT_PARAMETER;
+}
+
+// ==========================================================
+struct import_info {
+    const char* from;
+    const char* filename;
+    buffer_type statement;
+    const char* neededClass;
+    document_item_type* doc;
+    struct import_info* next;
+};
+
+document_item_type* g_document = NULL;
+import_info* g_imports = NULL;
+
+static void
+main_document_parsed(document_item_type* d)
+{
+    g_document = d;
+}
+
+static void
+main_import_parsed(buffer_type* statement)
+{
+    import_info* import = (import_info*)malloc(sizeof(import_info));
+    memset(import, 0, sizeof(import_info));
+    import->from = strdup(g_currentFilename);
+    import->statement.lineno = statement->lineno;
+    import->statement.data = strdup(statement->data);
+    import->statement.extra = NULL;
+    import->next = g_imports;
+    import->neededClass = parse_import_statement(statement->data);
+    g_imports = import;
+}
+
+static ParserCallbacks g_mainCallbacks = {
+    &main_document_parsed,
+    &main_import_parsed
+};
+
+char*
+parse_import_statement(const char* text)
+{
+    const char* end;
+    int len;
+
+    while (isspace(*text)) {
+        text++;
+    }
+    while (!isspace(*text)) {
+        text++;
+    }
+    while (isspace(*text)) {
+        text++;
+    }
+    end = text;
+    while (!isspace(*end) && *end != ';') {
+        end++;
+    }
+    len = end-text;
+
+    char* rv = (char*)malloc(len+1);
+    memcpy(rv, text, len);
+    rv[len] = '\0';
+
+    return rv;
+}
+
+// ==========================================================
+static void
+import_import_parsed(buffer_type* statement)
+{
+}
+
+static ParserCallbacks g_importCallbacks = {
+    &main_document_parsed,
+    &import_import_parsed
+};
+
+// ==========================================================
+static int
+check_filename(const char* filename, const char* package, buffer_type* name)
+{
+    const char* p;
+    string expected;
+    string fn;
+    size_t len;
+    char cwd[MAXPATHLEN];
+    bool valid = false;
+
+#ifdef HAVE_WINDOWS_PATHS
+    if (isalpha(filename[0]) && filename[1] == ':'
+        && filename[2] == OS_PATH_SEPARATOR) {
+#else
+    if (filename[0] == OS_PATH_SEPARATOR) {
+#endif
+        fn = filename;
+    } else {
+        fn = getcwd(cwd, sizeof(cwd));
+        len = fn.length();
+        if (fn[len-1] != OS_PATH_SEPARATOR) {
+            fn += OS_PATH_SEPARATOR;
+        }
+        fn += filename;
+    }
+
+    if (package) {
+        expected = package;
+        expected += '.';
+    }
+
+    len = expected.length();
+    for (size_t i=0; i<len; i++) {
+        if (expected[i] == '.') {
+            expected[i] = OS_PATH_SEPARATOR;
+        }
+    }
+
+    p = strchr(name->data, '.');
+    len = p ? p-name->data : strlen(name->data);
+    expected.append(name->data, len);
+    
+    expected += ".aidl";
+
+    len = fn.length();
+    valid = (len >= expected.length());
+
+    if (valid) {
+        p = fn.c_str() + (len - expected.length());
+
+#ifdef HAVE_WINDOWS_PATHS
+        if (OS_PATH_SEPARATOR != '/') {
+            // Input filename under cygwin most likely has / separators
+            // whereas the expected string uses \\ separators. Adjust
+            // them accordingly.
+          for (char *c = const_cast<char *>(p); *c; ++c) {
+                if (*c == '/') *c = OS_PATH_SEPARATOR;
+            }
+        }
+#endif
+
+#ifdef OS_CASE_SENSITIVE
+        valid = (expected == p);
+#else
+        valid = !strcasecmp(expected.c_str(), p);
+#endif
+    }
+
+    if (!valid) {
+        fprintf(stderr, "%s:%d interface %s should be declared in a file"
+                " called %s.\n",
+                filename, name->lineno, name->data, expected.c_str());
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+check_filenames(const char* filename, document_item_type* items)
+{
+    int err = 0;
+    while (items) {
+        if (items->item_type == PARCELABLE_TYPE) {
+            parcelable_type* p = (parcelable_type*)items;
+            err |= check_filename(filename, p->package, &p->name);
+        }
+        else if (items->item_type == INTERFACE_TYPE) {
+            interface_type* c = (interface_type*)items;
+            err |= check_filename(filename, c->package, &c->name);
+        }
+        else {
+            fprintf(stderr, "aidl: internal error unkown document type %d.\n",
+                        items->item_type);
+            return 1;
+        }
+        items = items->next;
+    }
+    return err;
+}
+
+// ==========================================================
+static const char*
+kind_to_string(int kind)
+{
+    switch (kind)
+    {
+        case Type::INTERFACE:
+            return "an interface";
+        case Type::PARCELABLE:
+            return "a parcelable";
+        default:
+            return "ERROR";
+    }
+}
+
+static char*
+rfind(char* str, char c)
+{
+    char* p = str + strlen(str) - 1;
+    while (p >= str) {
+        if (*p == c) {
+            return p;
+        }
+        p--;
+    }
+    return NULL;
+}
+
+static int
+gather_types(const char* filename, document_item_type* items)
+{
+    int err = 0;
+    while (items) {
+        Type* type;
+        if (items->item_type == PARCELABLE_TYPE) {
+            parcelable_type* p = (parcelable_type*)items;
+            type = new ParcelableType(p->package ? p->package : "",
+                            p->name.data, false, filename, p->name.lineno);
+        }
+        else if (items->item_type == INTERFACE_TYPE) {
+            interface_type* c = (interface_type*)items;
+            type = new InterfaceType(c->package ? c->package : "",
+                            c->name.data, false, c->oneway,
+                            filename, c->name.lineno);
+        }
+        else {
+            fprintf(stderr, "aidl: internal error %s:%d\n", __FILE__, __LINE__);
+            return 1;
+        }
+
+        Type* old = NAMES.Find(type->QualifiedName());
+        if (old == NULL) {
+            NAMES.Add(type);
+
+            if (items->item_type == INTERFACE_TYPE) {
+                // for interfaces, also add the stub and proxy types, we don't
+                // bother checking these for duplicates, because the parser
+                // won't let us do it.
+                interface_type* c = (interface_type*)items;
+
+                string name = c->name.data;
+                name += ".Stub";
+                Type* stub = new Type(c->package ? c->package : "",
+                                        name, Type::GENERATED, false, false,
+                                        filename, c->name.lineno);
+                NAMES.Add(stub);
+
+                name = c->name.data;
+                name += ".Stub.Proxy";
+                Type* proxy = new Type(c->package ? c->package : "",
+                                        name, Type::GENERATED, false, false,
+                                        filename, c->name.lineno);
+                NAMES.Add(proxy);
+            }
+        } else {
+            if (old->Kind() == Type::BUILT_IN) {
+                fprintf(stderr, "%s:%d attempt to redefine built in class %s\n",
+                            filename, type->DeclLine(),
+                            type->QualifiedName().c_str());
+                err = 1;
+            }
+            else if (type->Kind() != old->Kind()) {
+                const char* oldKind = kind_to_string(old->Kind());
+                const char* newKind = kind_to_string(type->Kind());
+
+                fprintf(stderr, "%s:%d attempt to redefine %s as %s,\n",
+                            filename, type->DeclLine(),
+                            type->QualifiedName().c_str(), newKind);
+                fprintf(stderr, "%s:%d    previously defined here as %s.\n",
+                            old->DeclFile().c_str(), old->DeclLine(), oldKind);
+                err = 1;
+            }
+        }
+
+        items = items->next;
+    }
+    return err;
+}
+
+// ==========================================================
+static bool
+matches_keyword(const char* str)
+{
+    static const char* KEYWORDS[] = { "abstract", "assert", "boolean", "break",
+        "byte", "case", "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends", "final",
+        "finally", "float", "for", "goto", "if", "implements", "import",
+        "instanceof", "int", "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short", "static",
+        "strictfp", "super", "switch", "synchronized", "this", "throw",
+        "throws", "transient", "try", "void", "volatile", "while",
+        "true", "false", "null",
+        NULL
+    };
+    const char** k = KEYWORDS;
+    while (*k) {
+        if (0 == strcmp(str, *k)) {
+            return true;
+        }
+        k++;
+    }
+    return false;
+}
+
+static int
+check_method(const char* filename, method_type* m)
+{
+    int err = 0;
+
+    // return type
+    Type* returnType = NAMES.Search(m->type.type.data);
+    if (returnType == NULL) {
+        fprintf(stderr, "%s:%d unknown return type %s\n", filename,
+                    m->type.type.lineno, m->type.type.data);
+        err = 1;
+        return err;
+    }
+
+    if (!returnType->CanBeMarshalled()) {
+        fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
+                    m->type.type.lineno, m->type.type.data);
+        err = 1;
+    }
+
+    if (m->type.dimension > 0 && !returnType->CanBeArray()) {
+        fprintf(stderr, "%s:%d return type %s%s can't be an array.\n", filename,
+                m->type.array_token.lineno, m->type.type.data,
+                m->type.array_token.data);
+        err = 1;
+    }
+
+    if (m->type.dimension > 1) {
+        fprintf(stderr, "%s:%d return type %s%s only one"
+                " dimensional arrays are supported\n", filename,
+                m->type.array_token.lineno, m->type.type.data,
+                m->type.array_token.data);
+        err = 1;
+    }
+
+    int index = 1;
+
+    arg_type* arg = m->args;
+    while (arg) {
+        Type* t = NAMES.Search(arg->type.type.data);
+
+        // check the arg type
+        if (t == NULL) {
+            fprintf(stderr, "%s:%d parameter %s (%d) unknown type %s\n",
+                    filename, m->type.type.lineno, arg->name.data, index,
+                    arg->type.type.data);
+            err = 1;
+            goto next;
+        }
+        
+        if (!t->CanBeMarshalled()) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n",
+                        filename, m->type.type.lineno, index,
+                        arg->type.type.data, arg->name.data);
+            err = 1;
+        }
+
+        if (arg->direction.data == NULL
+                && (arg->type.dimension != 0 || t->CanBeOutParameter())) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out"
+                                " parameter, so you must declare it as in,"
+                                " out or inout.\n",
+                        filename, m->type.type.lineno, index,
+                        arg->type.type.data, arg->name.data);
+            err = 1;
+        }
+
+        if (convert_direction(arg->direction.data) != IN_PARAMETER
+                && !t->CanBeOutParameter()
+                && arg->type.dimension == 0) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s %s' can only be an in"
+                            " parameter.\n",
+                        filename, m->type.type.lineno, index,
+                        arg->direction.data, arg->type.type.data,
+                        arg->name.data);
+            err = 1;
+        }
+
+        if (arg->type.dimension > 0 && !t->CanBeArray()) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' can't be an"
+                    " array.\n", filename,
+                    m->type.array_token.lineno, index, arg->direction.data,
+                    arg->type.type.data, arg->type.array_token.data,
+                    arg->name.data);
+            err = 1;
+        }
+
+        if (arg->type.dimension > 1) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' only one"
+                    " dimensional arrays are supported\n", filename,
+                    m->type.array_token.lineno, index, arg->direction.data,
+                    arg->type.type.data, arg->type.array_token.data,
+                    arg->name.data);
+            err = 1;
+        }
+
+        // check that the name doesn't match a keyword
+        if (matches_keyword(arg->name.data)) {
+            fprintf(stderr, "%s:%d parameter %d %s is named the same as a"
+                    " Java keyword\n",
+                    filename, m->name.lineno, index, arg->name.data);
+            err = 1;
+        }
+        
+next:
+        index++;
+        arg = arg->next;
+    }
+
+    return err;
+}
+
+static int
+check_types(const char* filename, document_item_type* items)
+{
+    int err = 0;
+    while (items) {
+        // (nothing to check for PARCELABLE_TYPE)
+        if (items->item_type == INTERFACE_TYPE) {
+            map<string,method_type*> methodNames;
+            interface_type* c = (interface_type*)items;
+
+            interface_item_type* member = c->interface_items;
+            while (member) {
+                if (member->item_type == METHOD_TYPE) {
+                    method_type* m = (method_type*)member;
+
+                    err |= check_method(filename, m);
+
+                    // prevent duplicate methods
+                    if (methodNames.find(m->name.data) == methodNames.end()) {
+                        methodNames[m->name.data] = m;
+                    } else {
+                        fprintf(stderr,"%s:%d attempt to redefine method %s,\n",
+                                filename, m->name.lineno, m->name.data);
+                        method_type* old = methodNames[m->name.data];
+                        fprintf(stderr, "%s:%d    previously defined here.\n",
+                                filename, old->name.lineno);
+                        err = 1;
+                    }
+                }
+                member = member->next;
+            }
+        }
+
+        items = items->next;
+    }
+    return err;
+}
+
+// ==========================================================
+static int
+exactly_one_interface(const char* filename, const document_item_type* items, const Options& options,
+                      bool* onlyParcelable)
+{
+    if (items == NULL) {
+        fprintf(stderr, "%s: file does not contain any interfaces\n",
+                            filename);
+        return 1;
+    }
+
+    const document_item_type* next = items->next;
+    if (items->next != NULL) {
+        int lineno = -1;
+        if (next->item_type == INTERFACE_TYPE) {
+            lineno = ((interface_type*)next)->interface_token.lineno;
+        }
+        else if (next->item_type == PARCELABLE_TYPE) {
+            lineno = ((parcelable_type*)next)->parcelable_token.lineno;
+        }
+        fprintf(stderr, "%s:%d aidl can only handle one interface per file\n",
+                            filename, lineno);
+        return 1;
+    }
+
+    if (items->item_type == PARCELABLE_TYPE) {
+        *onlyParcelable = true;
+        if (options.failOnParcelable) {
+            fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not"
+                            " parcelables,\n", filename,
+                            ((parcelable_type*)items)->parcelable_token.lineno);
+            fprintf(stderr, "%s:%d .aidl files that only declare parcelables "
+                            "don't need to go in the Makefile.\n", filename,
+                            ((parcelable_type*)items)->parcelable_token.lineno);
+            return 1;
+        }
+    } else {
+        *onlyParcelable = false;
+    }
+
+    return 0;
+}
+
+// ==========================================================
+void
+generate_dep_file(const Options& options)
+{
+   /* we open the file in binary mode to ensure that the same output is
+    * generated on all platforms !!
+    */
+    FILE* to = fopen(options.depFileName.c_str(), "wb");
+    if (to == NULL) {
+        return;
+    }
+
+    const char* slash = "\\";
+    import_info* import = g_imports;
+    if (import == NULL) {
+        slash = "";
+    }
+
+    fprintf(to, "%s: \\\n", options.outputFileName.c_str());
+    fprintf(to, "  %s %s\n", options.inputFileName.c_str(), slash);
+
+    while (import) {
+        if (import->next == NULL) {
+            slash = "";
+        }
+        if (import->filename) {
+            fprintf(to, "  %s %s\n", import->filename, slash);
+        }
+        import = import->next;
+    }
+
+    fprintf(to, "\n");
+
+    fclose(to);
+}
+
+// ==========================================================
+static int
+parse_preprocessed_file(const string& filename)
+{
+    int err;
+
+    FILE* f = fopen(filename.c_str(), "rb");
+    if (f == NULL) {
+        fprintf(stderr, "aidl: can't open preprocessd file: %s\n",
+                filename.c_str());
+        return 1;
+    }
+
+    int lineno = 1;
+    char line[1024];
+    char type[1024];
+    char fullname[1024];
+    while (fgets(line, sizeof(line), f)) {
+        // skip comments and empty lines
+        if (!line[0] || strncmp(line, "//", 2) == 0) {
+          continue;
+        }
+
+        sscanf(line, "%s %[^; \r\n\t];", type, fullname);
+
+        char* packagename;
+        char* classname = rfind(fullname, '.');
+        if (classname != NULL) {
+            *classname = '\0';
+            classname++;
+            packagename = fullname;
+        } else {
+            classname = fullname;
+            packagename = NULL;
+        }
+
+        //printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno,
+        //        type, packagename, classname);
+        document_item_type* doc;
+        
+        if (0 == strcmp("parcelable", type)) {
+            parcelable_type* parcl = (parcelable_type*)malloc(
+                    sizeof(parcelable_type));
+            memset(parcl, 0, sizeof(parcelable_type));
+            parcl->document_item.item_type = PARCELABLE_TYPE;
+            parcl->parcelable_token.lineno = lineno;
+            parcl->parcelable_token.data = strdup(type);
+            parcl->package = packagename ? strdup(packagename) : NULL;
+            parcl->name.lineno = lineno;
+            parcl->name.data = strdup(classname);
+            parcl->semicolon_token.lineno = lineno;
+            parcl->semicolon_token.data = strdup(";");
+            doc = (document_item_type*)parcl;
+        }
+        else if (0 == strcmp("interface", type)) {
+            interface_type* iface = (interface_type*)malloc(
+                    sizeof(interface_type));
+            memset(iface, 0, sizeof(interface_type));
+            iface->document_item.item_type = INTERFACE_TYPE;
+            iface->interface_token.lineno = lineno;
+            iface->interface_token.data = strdup(type);
+            iface->package = packagename ? strdup(packagename) : NULL;
+            iface->name.lineno = lineno;
+            iface->name.data = strdup(classname);
+            iface->open_brace_token.lineno = lineno;
+            iface->open_brace_token.data = strdup("{");
+            iface->close_brace_token.lineno = lineno;
+            iface->close_brace_token.data = strdup("}");
+            doc = (document_item_type*)iface;
+        }
+        else {
+            fprintf(stderr, "%s:%d: bad type in line: %s\n",
+                    filename.c_str(), lineno, line);
+            return 1;
+        }
+        err = gather_types(filename.c_str(), doc);
+        lineno++;
+    }
+
+    if (!feof(f)) {
+        fprintf(stderr, "%s:%d: error reading file, line to long.\n",
+                filename.c_str(), lineno);
+        return 1;
+    }
+
+    fclose(f);
+    return 0;
+}
+
+// ==========================================================
+static int
+compile_aidl(const Options& options)
+{
+    int err = 0, N;
+
+    set_import_paths(options.importPaths);
+
+    register_base_types();
+
+    // import the preprocessed file
+    N = options.preprocessedFiles.size();
+    for (int i=0; i<N; i++) {
+        const string& s = options.preprocessedFiles[i];
+        err |= parse_preprocessed_file(s);
+    }
+    if (err != 0) {
+        return err;
+    }
+
+    // parse the main file
+    g_callbacks = &g_mainCallbacks;
+    err = parse_aidl(options.inputFileName.c_str());
+    document_item_type* mainDoc = g_document;
+    g_document = NULL;
+
+    // parse the imports
+    g_callbacks = &g_mainCallbacks;
+    import_info* import = g_imports;
+    while (import) {
+        if (NAMES.Find(import->neededClass) == NULL) {
+            import->filename = find_import_file(import->neededClass);
+            if (!import->filename) {
+                fprintf(stderr, "%s:%d: couldn't find import for class %s\n",
+                        import->from, import->statement.lineno,
+                        import->neededClass);
+                err |= 1;
+            } else {
+                err |= parse_aidl(import->filename);
+                import->doc = g_document;
+                if (import->doc == NULL) {
+                    err |= 1;
+                }
+            }
+        }
+        import = import->next;
+    }
+    // bail out now if parsing wasn't successful
+    if (err != 0 || mainDoc == NULL) {
+        //fprintf(stderr, "aidl: parsing failed, stopping.\n");
+        return 1;
+    }
+
+    // complain about ones that aren't in the right files
+    err |= check_filenames(options.inputFileName.c_str(), mainDoc);
+    import = g_imports;
+    while (import) {
+        err |= check_filenames(import->filename, import->doc);
+        import = import->next;
+    }
+
+    // gather the types that have been declared
+    err |= gather_types(options.inputFileName.c_str(), mainDoc);
+    import = g_imports;
+    while (import) {
+        err |= gather_types(import->filename, import->doc);
+        import = import->next;
+    }
+
+#if 0
+    printf("---- main doc ----\n");
+    test_document(mainDoc);
+
+    import = g_imports;
+    while (import) {
+        printf("---- import doc ----\n");
+        test_document(import->doc);
+        import = import->next;
+    }
+    NAMES.Dump();
+#endif
+
+    // check the referenced types in mainDoc to make sure we've imported them
+    err |= check_types(options.inputFileName.c_str(), mainDoc);
+
+    // finally, there really only needs to be one thing in mainDoc, and it
+    // needs to be an interface.
+    bool onlyParcelable = false;
+    err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable);
+
+    // after this, there shouldn't be any more errors because of the
+    // input.
+    if (err != 0 || mainDoc == NULL) {
+        return 1;
+    }
+
+    // they didn't ask to fail on parcelables, so just exit quietly.
+    if (onlyParcelable && !options.failOnParcelable) {
+        return 0;
+    }
+
+    // if we were asked to, generate a make dependency file
+    if (options.depFileName != "") {
+        generate_dep_file(options);
+    }
+
+    err = generate_java(options.outputFileName, options.inputFileName.c_str(),
+                        (interface_type*)mainDoc);
+
+    return err;
+}
+
+static int
+preprocess_aidl(const Options& options)
+{
+    vector<string> lines;
+    int err;
+
+    // read files
+    int N = options.filesToPreprocess.size();
+    for (int i=0; i<N; i++) {
+        g_callbacks = &g_mainCallbacks;
+        err = parse_aidl(options.filesToPreprocess[i].c_str());
+        if (err != 0) {
+            return err;
+        }
+        document_item_type* doc = g_document;
+        string line;
+        if (doc->item_type == PARCELABLE_TYPE) {
+            line = "parcelable ";
+            parcelable_type* parcelable = (parcelable_type*)doc;
+            if (parcelable->package) {
+                line += parcelable->package;
+                line += '.';
+            }
+            line += parcelable->name.data;
+        } else {
+            line = "interface ";
+            interface_type* iface = (interface_type*)doc;
+            if (iface->package) {
+                line += iface->package;
+                line += '.';
+            }
+            line += iface->name.data;
+        }
+        line += ";\n";
+        lines.push_back(line);
+    }
+
+    // write preprocessed file
+    int fd = open( options.outputFileName.c_str(), 
+                   O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
+#ifdef HAVE_MS_C_RUNTIME
+                   _S_IREAD|_S_IWRITE);
+#else    
+                   S_IRUSR|S_IWUSR|S_IRGRP);
+#endif            
+    if (fd == -1) {
+        fprintf(stderr, "aidl: could not open file for write: %s\n",
+                options.outputFileName.c_str());
+        return 1;
+    }
+
+    N = lines.size();
+    for (int i=0; i<N; i++) {
+        const string& s = lines[i];
+        int len = s.length();
+        if (len != write(fd, s.c_str(), len)) {
+            fprintf(stderr, "aidl: error writing to file %s\n",
+                options.outputFileName.c_str());
+            close(fd);
+            unlink(options.outputFileName.c_str());
+            return 1;
+        }
+    }
+
+    close(fd);
+    return 0;
+}
+
+// ==========================================================
+int
+main(int argc, const char **argv)
+{
+    int err = 0;
+
+    Options options;
+    int result = parse_options(argc, argv, &options);
+    if (result) {
+        return result;
+    }
+
+    switch (options.task)
+    {
+        case COMPILE_AIDL:
+            return compile_aidl(options);
+        case PREPROCESS_AIDL:
+            return preprocess_aidl(options);
+    }
+    fprintf(stderr, "aidl: internal error\n");
+    return 1;
+}
+
+
diff --git a/tools/aidl/aidl_language.cpp b/tools/aidl/aidl_language.cpp
new file mode 100644
index 0000000..cd6a3bd
--- /dev/null
+++ b/tools/aidl/aidl_language.cpp
@@ -0,0 +1,20 @@
+#include "aidl_language.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_MS_C_RUNTIME
+int isatty(int  fd)
+{
+    return (fd == 0);
+}
+#endif
+
+#if 0
+ParserCallbacks k_parserCallbacks = {
+    NULL
+};
+#endif
+
+ParserCallbacks* g_callbacks = NULL; // &k_parserCallbacks;
+
diff --git a/tools/aidl/aidl_language.h b/tools/aidl/aidl_language.h
new file mode 100644
index 0000000..9ca5deb
--- /dev/null
+++ b/tools/aidl/aidl_language.h
@@ -0,0 +1,159 @@
+#ifndef DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H
+#define DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H
+
+
+typedef enum {
+    NO_EXTRA_TEXT = 0,
+    SHORT_COMMENT,
+    LONG_COMMENT,
+    COPY_TEXT,
+    WHITESPACE
+} which_extra_text;
+
+typedef struct extra_text_type {
+    unsigned lineno;
+    which_extra_text which;
+    char* data; 
+    unsigned len;
+    struct extra_text_type* next;
+} extra_text_type;
+
+typedef struct buffer_type {
+    unsigned lineno;
+    unsigned token;
+    char *data;
+    extra_text_type* extra;
+} buffer_type;
+
+typedef struct type_type {
+    buffer_type type;
+    buffer_type array_token;
+    int dimension;
+} type_type;
+
+typedef struct arg_type {
+    buffer_type comma_token; // empty in the first one in the list
+    buffer_type direction;
+    type_type type;
+    buffer_type name;
+    struct arg_type *next;
+} arg_type;
+
+enum {
+    METHOD_TYPE
+};
+
+typedef struct interface_item_type {
+    unsigned item_type;
+    struct interface_item_type* next;
+} interface_item_type;
+
+typedef struct method_type {
+    interface_item_type interface_item;
+    type_type type;
+    bool oneway;
+    buffer_type oneway_token;
+    buffer_type name;
+    buffer_type open_paren_token;
+    arg_type* args;
+    buffer_type close_paren_token;
+    // XXX missing comments/copy text here
+    buffer_type semicolon_token;
+    buffer_type* comments_token; // points into this structure, DO NOT DELETE
+} method_type;
+
+enum {
+    PARCELABLE_TYPE = 12,
+    INTERFACE_TYPE
+};
+
+typedef struct document_item_type {
+    unsigned item_type;
+    struct document_item_type* next;
+} document_item_type;
+
+typedef struct parcelable_type {
+    document_item_type document_item;
+    buffer_type parcelable_token;
+    char* package;
+    buffer_type name;
+    buffer_type semicolon_token;
+} parcelable_type;
+
+typedef struct interface_type {
+    document_item_type document_item;
+    buffer_type interface_token;
+    bool oneway;
+    buffer_type oneway_token;
+    char* package;
+    buffer_type name;
+    buffer_type open_brace_token;
+    interface_item_type* interface_items;
+    buffer_type close_brace_token;
+    buffer_type* comments_token; // points into this structure, DO NOT DELETE
+} interface_type;
+
+typedef union lexer_type {
+    buffer_type buffer;
+    type_type type;
+    arg_type *arg;
+    method_type* method;
+    interface_item_type* interface_item;
+    interface_type* interface_obj;
+    parcelable_type* parcelable;
+    document_item_type* document_item;
+} lexer_type;
+
+
+#define YYSTYPE lexer_type
+
+#if __cplusplus
+extern "C" {
+#endif
+
+int parse_aidl(char const *);
+
+// strips off the leading whitespace, the "import" text
+// also returns whether it's a local or system import
+// we rely on the input matching the import regex from below
+char* parse_import_statement(const char* text);
+
+// in, out or inout
+enum {
+    IN_PARAMETER = 1,
+    OUT_PARAMETER = 2,
+    INOUT_PARAMETER = 3
+};
+int convert_direction(const char* direction);
+
+// callbacks from within the parser
+// these functions all take ownership of the strings
+typedef struct ParserCallbacks {
+    void (*document)(document_item_type* items);
+    void (*import)(buffer_type* statement);
+} ParserCallbacks;
+
+extern ParserCallbacks* g_callbacks;
+
+// true if there was an error parsing, false otherwise
+extern int g_error;
+
+// the name of the file we're currently parsing
+extern char const* g_currentFilename;
+
+// the package name for our current file
+extern char const* g_currentPackage;
+
+typedef enum {
+    STATEMENT_INSIDE_INTERFACE
+} error_type;
+
+void init_buffer_type(buffer_type* buf, int lineno);
+
+
+#if __cplusplus
+}
+#endif
+
+
+#endif // DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H
diff --git a/tools/aidl/aidl_language_l.l b/tools/aidl/aidl_language_l.l
new file mode 100644
index 0000000..567b1cf
--- /dev/null
+++ b/tools/aidl/aidl_language_l.l
@@ -0,0 +1,210 @@
+%{
+#include "aidl_language.h"
+#include "aidl_language_y.h"
+#include "search_path.h"
+#include <string.h>
+#include <stdlib.h>
+
+extern YYSTYPE yylval;
+
+// comment and whitespace handling
+// these functions save a copy of the buffer
+static void begin_extra_text(unsigned lineno, which_extra_text which);
+static void append_extra_text(char* text);
+static extra_text_type* get_extra_text(void);   // you now own the object
+                                                // this returns
+static void drop_extra_text(void);
+
+// package handling
+static void do_package_statement(const char* importText);
+
+#define SET_BUFFER(t) \
+    do { \
+        yylval.buffer.lineno = yylineno; \
+        yylval.buffer.token = (t); \
+        yylval.buffer.data = strdup(yytext); \
+        yylval.buffer.extra = get_extra_text(); \
+    } while(0)
+
+%}
+
+%option yylineno
+%option noyywrap
+
+%x COPYING LONG_COMMENT
+
+identifier  [_a-zA-Z][_a-zA-Z0-9\.]*
+whitespace  ([ \t\n\r]+)
+brackets    \[{whitespace}?\]
+
+%%
+
+
+\%\%\{              { begin_extra_text(yylineno, COPY_TEXT); BEGIN(COPYING); }
+<COPYING>\}\%\%     { BEGIN(INITIAL); }
+<COPYING>.*\n       { append_extra_text(yytext); }
+<COPYING>.*         { append_extra_text(yytext); }
+<COPYING>\n+        { append_extra_text(yytext); }
+
+
+\/\*                            { begin_extra_text(yylineno, (which_extra_text)LONG_COMMENT);
+                                  BEGIN(LONG_COMMENT); }
+<LONG_COMMENT>[^*]*             { append_extra_text(yytext); }
+<LONG_COMMENT>\*+[^/]           { append_extra_text(yytext); }
+<LONG_COMMENT>\n                { append_extra_text(yytext); }
+<LONG_COMMENT>\**\/             { BEGIN(INITIAL); }
+
+^{whitespace}?import{whitespace}[^ \t\r\n]+{whitespace}?;  {
+                                                SET_BUFFER(IMPORT);
+                                                return IMPORT;
+                                            }
+^{whitespace}?package{whitespace}[^ \t\r\n]+{whitespace}?;  {
+                                                do_package_statement(yytext);
+                                                SET_BUFFER(PACKAGE);
+                                                return PACKAGE;
+                                            }
+<<EOF>>             { yyterminate(); }
+
+\/\/.*\n            { begin_extra_text(yylineno, SHORT_COMMENT);
+                        append_extra_text(yytext); }
+
+{whitespace}    { /* begin_extra_text(yylineno, WHITESPACE);
+                    append_extra_text(yytext); */ }
+
+;               { SET_BUFFER(';'); return ';'; }
+\{              { SET_BUFFER('{'); return '{'; }
+\}              { SET_BUFFER('}'); return '}'; }
+\(              { SET_BUFFER('('); return '('; }
+\)              { SET_BUFFER(')'); return ')'; }
+,               { SET_BUFFER(','); return ','; }
+
+    /* keywords */
+parcelable      { SET_BUFFER(PARCELABLE); return PARCELABLE; }
+interface       { SET_BUFFER(INTERFACE); return INTERFACE; }
+in              { SET_BUFFER(IN); return IN; }
+out             { SET_BUFFER(OUT); return OUT; }
+inout           { SET_BUFFER(INOUT); return INOUT; }
+oneway          { SET_BUFFER(ONEWAY); return ONEWAY; }
+
+{brackets}+     { SET_BUFFER(ARRAY); return ARRAY; }
+
+{identifier}                                        { SET_BUFFER(IDENTIFIER); return IDENTIFIER; }
+{identifier}\<{whitespace}*{identifier}({whitespace}*,{whitespace}*{identifier})*{whitespace}*\>    {
+                                                      SET_BUFFER(GENERIC); return GENERIC; }
+
+    /* syntax error! */
+.               { printf("UNKNOWN(%s)", yytext);
+                  yylval.buffer.lineno = yylineno;
+                  yylval.buffer.token = IDENTIFIER;
+                  yylval.buffer.data = strdup(yytext);
+                  return IDENTIFIER;
+                }
+
+%%
+
+// comment and whitespace handling
+// ================================================
+extra_text_type* g_extraText = NULL;
+extra_text_type* g_nextExtraText = NULL;
+
+void begin_extra_text(unsigned lineno, which_extra_text which)
+{
+    extra_text_type* text = (extra_text_type*)malloc(sizeof(extra_text_type));
+    text->lineno = lineno;
+    text->which = which;
+    text->data = NULL;
+    text->len = 0;
+    text->next = NULL;
+    if (g_nextExtraText == NULL) {
+        g_extraText = text;
+    } else {
+        g_nextExtraText->next = text;
+    }
+    g_nextExtraText = text;
+}
+
+void append_extra_text(char* text)
+{
+    if (g_nextExtraText->data == NULL) {
+        g_nextExtraText->data = strdup(text);
+        g_nextExtraText->len = strlen(text);
+    } else {
+        char* orig = g_nextExtraText->data;
+        unsigned oldLen = g_nextExtraText->len;
+        unsigned len = strlen(text);
+        g_nextExtraText->len += len;
+        g_nextExtraText->data = (char*)malloc(g_nextExtraText->len+1);
+        memcpy(g_nextExtraText->data, orig, oldLen);
+        memcpy(g_nextExtraText->data+oldLen, text, len);
+        g_nextExtraText->data[g_nextExtraText->len] = '\0';
+        free(orig);
+    }
+}
+
+extra_text_type*
+get_extra_text(void)
+{
+    extra_text_type* result = g_extraText;
+    g_extraText = NULL;
+    g_nextExtraText = NULL;
+    return result;
+}
+
+void drop_extra_text(void)
+{
+    extra_text_type* p = g_extraText;
+    while (p) {
+        extra_text_type* next = p->next;
+        free(p->data);
+        free(p);
+        free(next);
+    }
+    g_extraText = NULL;
+    g_nextExtraText = NULL;
+}
+
+
+// package handling
+// ================================================
+void do_package_statement(const char* importText)
+{
+    if (g_currentPackage) free((void*)g_currentPackage);
+    g_currentPackage = parse_import_statement(importText);
+}
+
+
+// main parse function
+// ================================================
+char const* g_currentFilename = NULL;
+char const* g_currentPackage = NULL;
+
+int yyparse(void);
+
+int parse_aidl(char const *filename)
+{
+    yyin = fopen(filename, "r");
+    if (yyin) {
+        char const* oldFilename = g_currentFilename;
+        char const* oldPackage = g_currentPackage;
+        g_currentFilename = strdup(filename);
+
+        g_error = 0;
+        yylineno = 1;
+        int rv = yyparse();
+        if (g_error != 0) {
+            rv = g_error;
+        }
+
+        free((void*)g_currentFilename);
+        g_currentFilename = oldFilename;
+        
+        if (g_currentPackage) free((void*)g_currentPackage);
+        g_currentPackage = oldPackage;
+
+        return rv;
+    } else {
+        fprintf(stderr, "aidl: unable to open file for read: %s\n", filename);
+        return 1;
+    }
+}
+
diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y
new file mode 100644
index 0000000..3d65f17
--- /dev/null
+++ b/tools/aidl/aidl_language_y.y
@@ -0,0 +1,288 @@
+%{
+#include "aidl_language.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int yyerror(char* errstr);
+int yylex(void);
+extern int yylineno;
+
+static int count_brackets(const char*);
+
+%}
+
+%token IMPORT
+%token PACKAGE
+%token IDENTIFIER
+%token GENERIC
+%token ARRAY
+%token PARCELABLE
+%token INTERFACE
+%token IN
+%token OUT
+%token INOUT
+%token ONEWAY
+
+%%
+document:
+        document_items                          { g_callbacks->document($1.document_item); }
+    |   headers document_items                  { g_callbacks->document($2.document_item); }
+    ;
+
+headers:
+        package                                 { }
+    |   imports                                 { }
+    |   package imports                         { }
+    ;
+
+package:
+        PACKAGE                                 { }
+    ;
+
+imports:
+        IMPORT                                  { g_callbacks->import(&($1.buffer)); }
+    |   IMPORT imports                          { g_callbacks->import(&($1.buffer)); }
+    ;
+
+document_items:
+                                                { $$.document_item = NULL; }
+    |   document_items declaration              {
+                                                    if ($2.document_item == NULL) {
+                                                        // error cases only
+                                                        $$ = $1;
+                                                    } else {
+                                                        document_item_type* p = $1.document_item;
+                                                        while (p && p->next) {
+                                                            p=p->next;
+                                                        }
+                                                        if (p) {
+                                                            p->next = (document_item_type*)$2.document_item;
+                                                            $$ = $1;
+                                                        } else {
+                                                            $$.document_item = (document_item_type*)$2.document_item;
+                                                        }
+                                                    }
+                                                }
+    | document_items error                      {
+                                                    fprintf(stderr, "%s:%d: syntax error don't know what to do with \"%s\"\n", g_currentFilename,
+                                                            $2.buffer.lineno, $2.buffer.data);
+                                                    $$ = $1;
+                                                }
+    ;
+
+declaration:
+        parcelable_decl                            { $$.document_item = (document_item_type*)$1.parcelable; }
+    |   interface_decl                             { $$.document_item = (document_item_type*)$1.interface_item; }
+    ;
+
+parcelable_decl:
+        PARCELABLE IDENTIFIER ';'                  { 
+                                                        parcelable_type* b = (parcelable_type*)malloc(sizeof(parcelable_type));
+                                                        b->document_item.item_type = PARCELABLE_TYPE;
+                                                        b->document_item.next = NULL;
+                                                        b->parcelable_token = $1.buffer;
+                                                        b->name = $2.buffer;
+                                                        b->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
+                                                        b->semicolon_token = $3.buffer;
+                                                        $$.parcelable = b;
+                                                    }
+    |   PARCELABLE ';'                              {
+                                                        fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name.\n",
+                                                                     g_currentFilename, $1.buffer.lineno);
+                                                        $$.parcelable = NULL;
+                                                    }
+    |   PARCELABLE error ';'                        {
+                                                        fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name, saw \"%s\".\n",
+                                                                     g_currentFilename, $2.buffer.lineno, $2.buffer.data);
+                                                        $$.parcelable = NULL;
+                                                    }
+    ;
+
+interface_header:
+        INTERFACE                                  {
+                                                        interface_type* c = (interface_type*)malloc(sizeof(interface_type));
+                                                        c->interface_token = $1.buffer;
+                                                        c->oneway = false;
+                                                        memset(&c->oneway_token, 0, sizeof(buffer_type));
+                                                        c->comments_token = &c->interface_token;
+                                                        $$.interface_obj = c;
+                                                   }
+    |   ONEWAY INTERFACE                           {
+                                                        interface_type* c = (interface_type*)malloc(sizeof(interface_type));
+                                                        c->interface_token = $2.buffer;
+                                                        c->oneway = true;
+                                                        c->oneway_token = $1.buffer;
+                                                        c->comments_token = &c->oneway_token;
+                                                        $$.interface_obj = c;
+                                                   }
+    ;
+
+interface_decl:
+        interface_header IDENTIFIER '{' interface_items '}' { 
+                                                        interface_type* c = $1.interface_obj;
+                                                        c->document_item.item_type = INTERFACE_TYPE;
+                                                        c->document_item.next = NULL;
+                                                        c->name = $2.buffer;
+                                                        c->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
+                                                        c->open_brace_token = $3.buffer;
+                                                        c->interface_items = $4.interface_item;
+                                                        c->close_brace_token = $5.buffer;
+                                                        $$.interface_obj = c;
+                                                    }
+    |   INTERFACE error '{' interface_items '}'     {
+                                                        fprintf(stderr, "%s:%d: syntax error in interface declaration.  Expected type name, saw \"%s\"\n",
+                                                                    g_currentFilename, $2.buffer.lineno, $2.buffer.data);
+                                                        $$.document_item = NULL;
+                                                    }
+    |   INTERFACE error '}'                             {
+                                                        fprintf(stderr, "%s:%d: syntax error in interface declaration.  Expected type name, saw \"%s\"\n",
+                                                                    g_currentFilename, $2.buffer.lineno, $2.buffer.data);
+                                                        $$.document_item = NULL;
+                                                    }
+
+    ;
+
+interface_items:
+                                                    { $$.interface_item = NULL; }
+    |   interface_items method_decl                 {
+                                                        interface_item_type* p=$1.interface_item;
+                                                        while (p && p->next) {
+                                                            p=p->next;
+                                                        }
+                                                        if (p) {
+                                                            p->next = (interface_item_type*)$2.method;
+                                                            $$ = $1;
+                                                        } else {
+                                                            $$.interface_item = (interface_item_type*)$2.method;
+                                                        }
+                                                    }
+    |   interface_items error ';'                   {
+                                                        fprintf(stderr, "%s:%d: syntax error before ';' (expected method declaration)\n",
+                                                                    g_currentFilename, $3.buffer.lineno);
+                                                        $$ = $1;
+                                                    }
+    ;
+
+method_decl:
+        type IDENTIFIER '(' arg_list ')' ';'  {
+                                                        method_type *method = (method_type*)malloc(sizeof(method_type));
+                                                        method->interface_item.item_type = METHOD_TYPE;
+                                                        method->interface_item.next = NULL;
+                                                        method->type = $1.type;
+                                                        method->oneway = false;
+                                                        memset(&method->oneway_token, 0, sizeof(buffer_type));
+                                                        method->name = $2.buffer;
+                                                        method->open_paren_token = $3.buffer;
+                                                        method->args = $4.arg;
+                                                        method->close_paren_token = $5.buffer;
+                                                        method->semicolon_token = $6.buffer;
+                                                        method->comments_token = &method->type.type;
+                                                        $$.method = method;
+                                                    }
+    |   ONEWAY type IDENTIFIER '(' arg_list ')' ';'  {
+                                                        method_type *method = (method_type*)malloc(sizeof(method_type));
+                                                        method->interface_item.item_type = METHOD_TYPE;
+                                                        method->interface_item.next = NULL;
+                                                        method->oneway = true;
+                                                        method->oneway_token = $1.buffer;
+                                                        method->type = $2.type;
+                                                        method->name = $3.buffer;
+                                                        method->open_paren_token = $4.buffer;
+                                                        method->args = $5.arg;
+                                                        method->close_paren_token = $6.buffer;
+                                                        method->semicolon_token = $7.buffer;
+                                                        method->comments_token = &method->oneway_token;
+                                                        $$.method = method;
+                                                    }
+    ;
+
+arg_list:
+                                { $$.arg = NULL; }
+    |   arg                     { $$ = $1; }
+    |   arg_list ',' arg        {
+                                    if ($$.arg != NULL) {
+                                        // only NULL on error
+                                        $$ = $1;
+                                        arg_type *p = $1.arg;
+                                        while (p && p->next) {
+                                            p=p->next;
+                                        }
+                                        $3.arg->comma_token = $2.buffer;
+                                        p->next = $3.arg;
+                                    }
+                                }
+    |   error                   {
+                                    fprintf(stderr, "%s:%d: syntax error in parameter list\n", g_currentFilename, $1.buffer.lineno);
+                                    $$.arg = NULL;
+                                }
+    ;
+
+arg:
+        direction type IDENTIFIER     {
+                                                arg_type* arg = (arg_type*)malloc(sizeof(arg_type));
+                                                memset(&arg->comma_token, 0, sizeof(buffer_type));
+                                                arg->direction = $1.buffer;
+                                                arg->type = $2.type;
+                                                arg->name = $3.buffer;
+                                                arg->next = NULL;
+                                                $$.arg = arg;
+                                      }
+    ;
+
+type:
+        IDENTIFIER              {
+                                    $$.type.type = $1.buffer;
+                                    init_buffer_type(&$$.type.array_token, yylineno);
+                                    $$.type.dimension = 0;
+                                }
+    |   IDENTIFIER ARRAY        {
+                                    $$.type.type = $1.buffer;
+                                    $$.type.array_token = $2.buffer;
+                                    $$.type.dimension = count_brackets($2.buffer.data);
+                                }
+    |   GENERIC                 {
+                                    $$.type.type = $1.buffer;
+                                    init_buffer_type(&$$.type.array_token, yylineno);
+                                    $$.type.dimension = 0;
+                                }
+    ;
+
+direction:
+                    { init_buffer_type(&$$.buffer, yylineno); }
+    |   IN          { $$.buffer = $1.buffer; }
+    |   OUT         { $$.buffer = $1.buffer; }
+    |   INOUT       { $$.buffer = $1.buffer; }
+    ;
+
+%%
+
+#include <ctype.h>
+#include <stdio.h>
+
+int g_error = 0;
+
+int yyerror(char* errstr)
+{
+    fprintf(stderr, "%s:%d: %s\n", g_currentFilename, yylineno, errstr);
+    g_error = 1;
+    return 1;
+}
+
+void init_buffer_type(buffer_type* buf, int lineno)
+{
+    buf->lineno = lineno;
+    buf->token = 0;
+    buf->data = NULL;
+    buf->extra = NULL;
+}
+
+static int count_brackets(const char* s)
+{
+    int n=0;
+    while (*s) {
+        if (*s == '[') n++;
+        s++;
+    }
+    return n;
+}
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
new file mode 100644
index 0000000..e3c0af0
--- /dev/null
+++ b/tools/aidl/generate_java.cpp
@@ -0,0 +1,652 @@
+#include "generate_java.h"
+#include "AST.h"
+#include "Type.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// =================================================
+class VariableFactory
+{
+public:
+    VariableFactory(const string& base); // base must be short
+    Variable* Get(Type* type);
+    Variable* Get(int index);
+private:
+    vector<Variable*> m_vars;
+    string m_base;
+    int m_index;
+};
+
+VariableFactory::VariableFactory(const string& base)
+    :m_base(base),
+     m_index(0)
+{
+}
+
+Variable*
+VariableFactory::Get(Type* type)
+{
+    char name[100];
+    sprintf(name, "%s%d", m_base.c_str(), m_index);
+    m_index++;
+    Variable* v = new Variable(type, name);
+    m_vars.push_back(v);
+    return v;
+}
+
+Variable*
+VariableFactory::Get(int index)
+{
+    return m_vars[index];
+}
+
+// =================================================
+class StubClass : public Class
+{
+public:
+    StubClass(Type* type, Type* interfaceType);
+    virtual ~StubClass();
+
+    Variable* transact_code;
+    Variable* transact_data;
+    Variable* transact_reply;
+    Variable* transact_flags;
+    SwitchStatement* transact_switch;
+private:
+    void make_as_interface(Type* interfaceType);
+};
+
+StubClass::StubClass(Type* type, Type* interfaceType)
+    :Class()
+{
+    this->comment = "/** Local-side IPC implementation stub class. */";
+    this->modifiers = PUBLIC | ABSTRACT | STATIC;
+    this->what = Class::CLASS;
+    this->type = type;
+    this->extends = BINDER_NATIVE_TYPE;
+    this->interfaces.push_back(interfaceType);
+
+    // descriptor
+    Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
+                            new Variable(STRING_TYPE, "DESCRIPTOR"));
+    descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
+    this->elements.push_back(descriptor);
+
+    // ctor
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->comment = "/** Construct the stub at attach it to the "
+                        "interface. */";
+        ctor->name = "Stub";
+        ctor->statements = new StatementBlock;
+    MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
+                            2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
+    ctor->statements->Add(attach);
+    this->elements.push_back(ctor);
+
+    // asInterface
+    make_as_interface(interfaceType);
+
+    // asBinder
+    Method* asBinder = new Method;
+        asBinder->modifiers = PUBLIC;
+        asBinder->returnType = IBINDER_TYPE;
+        asBinder->name = "asBinder";
+        asBinder->statements = new StatementBlock;
+    asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
+    this->elements.push_back(asBinder);
+
+    // onTransact
+    this->transact_code = new Variable(INT_TYPE, "code");
+    this->transact_data = new Variable(PARCEL_TYPE, "data");
+    this->transact_reply = new Variable(PARCEL_TYPE, "reply");
+    this->transact_flags = new Variable(INT_TYPE, "flags");
+    Method* onTransact = new Method;
+        onTransact->modifiers = PUBLIC;
+        onTransact->returnType = BOOLEAN_TYPE;
+        onTransact->name = "onTransact";
+        onTransact->parameters.push_back(this->transact_code);
+        onTransact->parameters.push_back(this->transact_data);
+        onTransact->parameters.push_back(this->transact_reply);
+        onTransact->parameters.push_back(this->transact_flags);
+        onTransact->statements = new StatementBlock;
+        onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+    this->elements.push_back(onTransact);
+    this->transact_switch = new SwitchStatement(this->transact_code);
+
+    onTransact->statements->Add(this->transact_switch);
+    MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
+                                    this->transact_code, this->transact_data,
+                                    this->transact_reply, this->transact_flags);
+    onTransact->statements->Add(new ReturnStatement(superCall));
+}
+
+StubClass::~StubClass()
+{
+}
+
+void
+StubClass::make_as_interface(Type *interfaceType)
+{
+    Variable* obj = new Variable(IBINDER_TYPE, "obj");
+
+    Method* m = new Method;
+        m->comment = "/**\n * Cast an IBinder object into an ";
+        m->comment += interfaceType->Name();
+        m->comment += " interface,\n";
+        m->comment += " * generating a proxy if needed.\n */";
+        m->modifiers = PUBLIC | STATIC;
+        m->returnType = interfaceType;
+        m->name = "asInterface";
+        m->parameters.push_back(obj);
+        m->statements = new StatementBlock;
+
+    IfStatement* ifstatement = new IfStatement();
+        ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
+        ifstatement->statements = new StatementBlock;
+        ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
+    m->statements->Add(ifstatement);
+
+    // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
+    MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
+    queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
+    IInterfaceType* iinType = new IInterfaceType();
+    Variable *iin = new Variable(iinType, "iin");
+    VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, iinType);
+    m->statements->Add(iinVd);
+
+    // Ensure the instance type of the local object is as expected.
+    // One scenario where this is needed is if another package (with a
+    // different class loader) runs in the same process as the service.
+
+    // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
+    Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
+    Comparison* instOfCheck = new Comparison(iin, " instanceof ",
+            new LiteralExpression(interfaceType->QualifiedName()));
+    IfStatement* instOfStatement = new IfStatement();
+        instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
+        instOfStatement->statements = new StatementBlock;
+        instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
+    m->statements->Add(instOfStatement);
+
+    string proxyType = interfaceType->QualifiedName();
+    proxyType += ".Stub.Proxy";
+    NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
+    ne->arguments.push_back(obj);
+    m->statements->Add(new ReturnStatement(ne));
+
+    this->elements.push_back(m);
+}
+
+
+
+// =================================================
+class ProxyClass : public Class
+{
+public:
+    ProxyClass(Type* type, InterfaceType* interfaceType);
+    virtual ~ProxyClass();
+
+    Variable* mRemote;
+    bool mOneWay;
+};
+
+ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
+    :Class()
+{
+    this->modifiers = PRIVATE | STATIC;
+    this->what = Class::CLASS;
+    this->type = type;
+    this->interfaces.push_back(interfaceType);
+
+    mOneWay = interfaceType->OneWay();
+
+    // IBinder mRemote
+    mRemote = new Variable(IBINDER_TYPE, "mRemote");
+    this->elements.push_back(new Field(PRIVATE, mRemote));
+
+    // Proxy()
+    Variable* remote = new Variable(IBINDER_TYPE, "remote");
+    Method* ctor = new Method;
+        ctor->name = "Proxy";
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(remote);
+    ctor->statements->Add(new Assignment(mRemote, remote));
+    this->elements.push_back(ctor);
+
+    // IBinder asBinder()
+    Method* asBinder = new Method;
+        asBinder->modifiers = PUBLIC;
+        asBinder->returnType = IBINDER_TYPE;
+        asBinder->name = "asBinder";
+        asBinder->statements = new StatementBlock;
+    asBinder->statements->Add(new ReturnStatement(mRemote));
+    this->elements.push_back(asBinder);
+}
+
+ProxyClass::~ProxyClass()
+{
+}
+
+// =================================================
+static string
+gather_comments(extra_text_type* extra)
+{
+    string s;
+    while (extra) {
+        if (extra->which == SHORT_COMMENT) {
+            s += extra->data;
+        }
+        else if (extra->which == LONG_COMMENT) {
+            s += "/*";
+            s += extra->data;
+            s += "*/";
+        }
+        extra = extra->next;
+    }
+    return s;
+}
+
+static string
+append(const char* a, const char* b)
+{
+    string s = a;
+    s += b;
+    return s;
+}
+
+static void
+generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    Variable* len = new Variable(INT_TYPE, v->name + "_length");
+    addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
+    IfStatement* lencheck = new IfStatement();
+    lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
+    lencheck->statements->Add(new Assignment(v, NULL_VALUE));
+    lencheck->elseif = new IfStatement();
+    lencheck->elseif->statements->Add(new Assignment(v,
+                new NewArrayExpression(t, len)));
+    addTo->Add(lencheck);
+}
+
+static void
+generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel, int flags)
+{
+    if (v->dimension == 0) {
+        t->WriteToParcel(addTo, v, parcel, flags);
+    }
+    if (v->dimension == 1) {
+        t->WriteArrayToParcel(addTo, v, parcel, flags);
+    }
+}
+
+static void
+generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    if (v->dimension == 0) {
+        t->CreateFromParcel(addTo, v, parcel);
+    }
+    if (v->dimension == 1) {
+        t->CreateArrayFromParcel(addTo, v, parcel);
+    }
+}
+
+static void
+generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    if (v->dimension == 0) {
+        t->ReadFromParcel(addTo, v, parcel);
+    }
+    if (v->dimension == 1) {
+        t->ReadArrayFromParcel(addTo, v, parcel);
+    }
+}
+
+
+static void
+generate_method(const method_type* method, Class* interface,
+                    StubClass* stubClass, ProxyClass* proxyClass, int index)
+{
+    arg_type* arg;
+    int i;
+    bool hasOutParams = false;
+
+    const bool oneway = proxyClass->mOneWay || method->oneway;
+
+    // == the TRANSACT_ constant =============================================
+    string transactCodeName = "TRANSACTION_";
+    transactCodeName += method->name.data;
+
+    char transactCodeValue[50];
+    sprintf(transactCodeValue, "(IBinder.FIRST_CALL_TRANSACTION + %d)", index);
+
+    Field* transactCode = new Field(STATIC | FINAL,
+                            new Variable(INT_TYPE, transactCodeName));
+    transactCode->value = transactCodeValue;
+    stubClass->elements.push_back(transactCode);
+
+    // == the declaration in the interface ===================================
+    Method* decl = new Method;
+        decl->comment = gather_comments(method->comments_token->extra);
+        decl->modifiers = PUBLIC;
+        decl->returnType = NAMES.Search(method->type.type.data);
+        decl->returnTypeDimension = method->type.dimension;
+        decl->name = method->name.data;
+
+    arg = method->args;
+    while (arg != NULL) {
+        decl->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+        arg = arg->next;
+    }
+
+    decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+
+    interface->elements.push_back(decl);
+
+    // == the stub method ====================================================
+
+    Case* c = new Case(transactCodeName);
+
+    MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
+
+    // interface token validation is the very first thing we do
+    c->statements->Add(new MethodCall(stubClass->transact_data,
+            "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
+
+    // args
+    VariableFactory stubArgs("_arg");
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(t);
+        v->dimension = arg->type.dimension;
+
+        c->statements->Add(new VariableDeclaration(v));
+
+        if (convert_direction(arg->direction.data) & IN_PARAMETER) {
+            generate_create_from_parcel(t, c->statements, v,
+                    stubClass->transact_data);
+        } else {
+            if (arg->type.dimension == 0) {
+                c->statements->Add(new Assignment(
+                                                v, new NewExpression(v->type)));
+            }
+            else if (arg->type.dimension == 1) {
+                generate_new_array(v->type, c->statements, v,
+                        stubClass->transact_data);
+            }
+            else {
+                fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
+                        __LINE__);
+            }
+        }
+
+        realCall->arguments.push_back(v);
+
+        arg = arg->next;
+    }
+
+    // the real call
+    Variable* _result = NULL;
+    if (0 == strcmp(method->type.type.data, "void")) {
+        c->statements->Add(realCall);
+
+        if (!oneway) {
+            // report that there were no exceptions
+            MethodCall* ex = new MethodCall(stubClass->transact_reply,
+                    "writeNoException", 0);
+            c->statements->Add(ex);
+        }
+    } else {
+        _result = new Variable(decl->returnType, "_result",
+                                decl->returnTypeDimension);
+        c->statements->Add(new VariableDeclaration(_result, realCall));
+
+        if (!oneway) {
+            // report that there were no exceptions
+            MethodCall* ex = new MethodCall(stubClass->transact_reply,
+                    "writeNoException", 0);
+            c->statements->Add(ex);
+        }
+
+        // marshall the return value
+        generate_write_to_parcel(decl->returnType, c->statements, _result,
+                                    stubClass->transact_reply,
+                                    Type::PARCELABLE_WRITE_RETURN_VALUE);
+    }
+
+    // out parameters
+    i = 0;
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(i++);
+
+        if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+            generate_write_to_parcel(t, c->statements, v,
+                                stubClass->transact_reply,
+                                Type::PARCELABLE_WRITE_RETURN_VALUE);
+            hasOutParams = true;
+        }
+
+        arg = arg->next;
+    }
+
+    // return true
+    c->statements->Add(new ReturnStatement(TRUE_VALUE));
+    stubClass->transact_switch->cases.push_back(c);
+
+    // == the proxy method ===================================================
+    Method* proxy = new Method;
+        proxy->comment = gather_comments(method->comments_token->extra);
+        proxy->modifiers = PUBLIC;
+        proxy->returnType = NAMES.Search(method->type.type.data);
+        proxy->returnTypeDimension = method->type.dimension;
+        proxy->name = method->name.data;
+        proxy->statements = new StatementBlock;
+        arg = method->args;
+        while (arg != NULL) {
+            proxy->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+            arg = arg->next;
+        }
+        proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+    proxyClass->elements.push_back(proxy);
+
+    // the parcels
+    Variable* _data = new Variable(PARCEL_TYPE, "_data");
+    proxy->statements->Add(new VariableDeclaration(_data,
+                                new MethodCall(PARCEL_TYPE, "obtain")));
+    Variable* _reply = NULL;
+    if (!oneway) {
+        _reply = new Variable(PARCEL_TYPE, "_reply");
+        proxy->statements->Add(new VariableDeclaration(_reply,
+                                    new MethodCall(PARCEL_TYPE, "obtain")));
+    }
+
+    // the return value
+    _result = NULL;
+    if (0 != strcmp(method->type.type.data, "void")) {
+        _result = new Variable(proxy->returnType, "_result",
+                method->type.dimension);
+        proxy->statements->Add(new VariableDeclaration(_result));
+    }
+
+    // try and finally
+    TryStatement* tryStatement = new TryStatement();
+    proxy->statements->Add(tryStatement);
+    FinallyStatement* finallyStatement = new FinallyStatement();
+    proxy->statements->Add(finallyStatement);
+
+    // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
+    tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
+            1, new LiteralExpression("DESCRIPTOR")));
+
+    // the parameters
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+        int dir = convert_direction(arg->direction.data);
+        if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
+            IfStatement* checklen = new IfStatement();
+            checklen->expression = new Comparison(v, "==", NULL_VALUE);
+            checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
+                        new LiteralExpression("-1")));
+            checklen->elseif = new IfStatement();
+            checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
+                        1, new FieldVariable(v, "length")));
+            tryStatement->statements->Add(checklen);
+        }
+        else if (dir & IN_PARAMETER) {
+            generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
+        }
+        arg = arg->next;
+    }
+
+    // the transact call
+    MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
+                            new LiteralExpression("Stub." + transactCodeName),
+                            _data, _reply ? _reply : NULL_VALUE,
+                            new LiteralExpression(
+                                oneway ? "IBinder.FLAG_ONEWAY" : "0"));
+    tryStatement->statements->Add(call);
+
+    // throw back exceptions.
+    if (_reply) {
+        MethodCall* ex = new MethodCall(_reply, "readException", 0);
+        tryStatement->statements->Add(ex);
+    }
+
+    // returning and cleanup
+    if (_reply != NULL) {
+        if (_result != NULL) {
+            generate_create_from_parcel(proxy->returnType,
+                                    tryStatement->statements, _result, _reply);
+        }
+
+        // the out/inout parameters
+        arg = method->args;
+        while (arg != NULL) {
+            Type* t = NAMES.Search(arg->type.type.data);
+            Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+            if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+                generate_read_from_parcel(t, tryStatement->statements,
+                                            v, _reply);
+            }
+            arg = arg->next;
+        }
+
+        finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
+    }
+    finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
+
+    if (_result != NULL) {
+        proxy->statements->Add(new ReturnStatement(_result));
+    }
+}
+
+static void
+generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
+{
+    // the interface descriptor transaction handler
+    Case* c = new Case("INTERFACE_TRANSACTION");
+    c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
+            1, new LiteralExpression("DESCRIPTOR")));
+    c->statements->Add(new ReturnStatement(TRUE_VALUE));
+    stub->transact_switch->cases.push_back(c);
+
+    // and the proxy-side method returning the descriptor directly
+    Method* getDesc = new Method;
+    getDesc->modifiers = PUBLIC;
+    getDesc->returnType = STRING_TYPE;
+    getDesc->returnTypeDimension = 0;
+    getDesc->name = "getInterfaceDescriptor";
+    getDesc->statements = new StatementBlock;
+    getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
+    proxy->elements.push_back(getDesc);
+}
+
+static Class*
+generate_interface_class(const interface_type* iface)
+{
+    InterfaceType* interfaceType = static_cast<InterfaceType*>(
+        NAMES.Find(iface->package, iface->name.data));
+
+    // the interface class
+    Class* interface = new Class;
+        interface->comment = gather_comments(iface->comments_token->extra);
+        interface->modifiers = PUBLIC;
+        interface->what = Class::INTERFACE;
+        interface->type = interfaceType;
+        interface->interfaces.push_back(IINTERFACE_TYPE);
+
+    // the stub inner class
+    StubClass* stub = new StubClass(
+        NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
+        interfaceType);
+    interface->elements.push_back(stub);
+
+    // the proxy inner class
+    ProxyClass* proxy = new ProxyClass(
+        NAMES.Find(iface->package,
+                         append(iface->name.data, ".Stub.Proxy").c_str()),
+        interfaceType);
+    stub->elements.push_back(proxy);
+
+    // stub and proxy support for getInterfaceDescriptor()
+    generate_interface_descriptors(stub, proxy);
+
+    // all the declared methods of the interface
+    int index = 0;
+    interface_item_type* item = iface->interface_items;
+    while (item != NULL) {
+        if (item->item_type == METHOD_TYPE) {
+            generate_method((method_type*)item, interface, stub, proxy, index);
+        }
+        item = item->next;
+        index++;
+    }
+
+    return interface;
+}
+
+int
+generate_java(const string& filename, const string& originalSrc,
+                interface_type* iface)
+{
+    Document* document = new Document;
+        document->comment = "";
+        if (iface->package) document->package = iface->package;
+        document->originalSrc = originalSrc;
+        document->classes.push_back(generate_interface_class(iface));
+
+//    printf("outputting... filename=%s\n", filename.c_str());
+    FILE* to;
+    if (filename == "-") {
+        to = stdout;
+    } else {
+       /* open file in binary mode to ensure that the tool produces the
+        * same output on all platforms !!
+        */
+        to = fopen(filename.c_str(), "wb");
+        if (to == NULL) {
+            fprintf(stderr, "unable to open %s for write\n", filename.c_str());
+            return 1;
+        }
+    }
+
+    document->Write(to);
+
+    fclose(to);
+    return 0;
+}
+
diff --git a/tools/aidl/generate_java.h b/tools/aidl/generate_java.h
new file mode 100644
index 0000000..203fe23
--- /dev/null
+++ b/tools/aidl/generate_java.h
@@ -0,0 +1,14 @@
+#ifndef GENERATE_JAVA_H
+#define GENERATE_JAVA_H
+
+#include "aidl_language.h"
+
+#include <string>
+
+using namespace std;
+
+int generate_java(const string& filename, const string& originalSrc,
+                interface_type* iface);
+
+#endif // GENERATE_JAVA_H
+
diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp
new file mode 100644
index 0000000..57b10ae
--- /dev/null
+++ b/tools/aidl/options.cpp
@@ -0,0 +1,138 @@
+
+#include "options.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+usage()
+{
+    fprintf(stderr,
+            "usage: aidl OPTIONS INPUT [OUTPUT]\n"
+            "       aidl --preprocess OUTPUT INPUT...\n"
+            "\n"
+            "OPTIONS:\n"
+            "   -I<DIR>  search path for import statements.\n"
+            "   -d<FILE> generate dependency file.\n"
+            "   -p<FILE> file created by --preprocess to import.\n"
+            "   -b       fail when trying to compile a parcelable.\n"
+            "\n"
+            "INPUT:\n"
+            "   An aidl interface file.\n"
+            "\n"
+            "OUTPUT:\n"
+            "   The generated interface files. If omitted, the input filename is used, with the .aidl extension changed to a .java extension.\n"
+           );
+    return 1;
+}
+
+int
+parse_options(int argc, const char* const* argv, Options *options)
+{
+    int i = 1;
+
+    if (argc >= 2 && 0 == strcmp(argv[1], "--preprocess")) {
+        if (argc < 4) {
+            return usage();
+        }
+        options->outputFileName = argv[2];
+        for (int i=3; i<argc; i++) {
+            options->filesToPreprocess.push_back(argv[i]);
+        }
+        options->task = PREPROCESS_AIDL;
+        return 0;
+    }
+
+    options->task = COMPILE_AIDL;
+    options->failOnParcelable = false;
+
+    // OPTIONS
+    while (i < argc) {
+        const char* s = argv[i];
+        int len = strlen(s);
+        if (s[0] == '-') {
+            if (len > 1) {
+                // -I<system-import-path>
+                if (s[1] == 'I') {
+                    if (len > 2) {
+                        options->importPaths.push_back(s+2);
+                    } else {
+                        fprintf(stderr, "-I option (%d) requires a path.\n", i);
+                        return usage();
+                    }
+                }
+                else if (s[1] == 'd') {
+                    if (len > 2) {
+                        options->depFileName = s+2;
+                    } else {
+                        fprintf(stderr, "-d option (%d) requires a file.\n", i);
+                        return usage();
+                    }
+                }
+                else if (s[1] == 'p') {
+                    if (len > 2) {
+                        options->preprocessedFiles.push_back(s+2);
+                    } else {
+                        fprintf(stderr, "-p option (%d) requires a file.\n", i);
+                        return usage();
+                    }
+                }
+                else if (len == 2 && s[1] == 'b') {
+                    options->failOnParcelable = true;
+                }
+                else {
+                    // s[1] is not known
+                    fprintf(stderr, "unknown option (%d): %s\n", i, s);
+                    return usage();
+                }
+            } else {
+                // len <= 1
+                fprintf(stderr, "unknown option (%d): %s\n", i, s);
+                return usage();
+            }
+        } else {
+            // s[0] != '-'
+            break;
+        }
+        i++;
+    }
+
+    // INPUT
+    if (i < argc) {
+        options->inputFileName = argv[i];
+        i++;
+    } else {
+        fprintf(stderr, "INPUT required\n");
+        return usage();
+    }
+
+    // OUTPUT
+    if (i < argc) {
+        options->outputFileName = argv[i];
+        i++;
+    } else {
+        // copy input into output and change the extension from .aidl to .java
+        options->outputFileName = options->inputFileName;
+        string::size_type pos = options->outputFileName.size()-5;
+        if (options->outputFileName.compare(pos, 5, ".aidl") == 0) {  // 5 = strlen(".aidl")
+            options->outputFileName.replace(pos, 5, ".java"); // 5 = strlen(".aidl")
+        } else {
+            fprintf(stderr, "INPUT is not an .aidl file.\n");
+            return usage();
+        }
+     }
+
+    // anything remaining?
+    if (i != argc) {
+        fprintf(stderr, "unknown option%s:", (i==argc-1?(const char*)"":(const char*)"s"));
+        for (; i<argc-1; i++) {
+            fprintf(stderr, " %s", argv[i]);
+        }
+        fprintf(stderr, "\n");
+        return usage();
+    }
+
+    return 0;
+}
+
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
new file mode 100644
index 0000000..dc3c45a
--- /dev/null
+++ b/tools/aidl/options.h
@@ -0,0 +1,33 @@
+#ifndef DEVICE_TOOLS_AIDL_H
+#define DEVICE_TOOLS_AIDL_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+enum {
+    COMPILE_AIDL,
+    PREPROCESS_AIDL
+};
+
+// This struct is the parsed version of the command line options
+struct Options
+{
+    int task;
+    bool failOnParcelable;
+    vector<string> importPaths;
+    vector<string> preprocessedFiles;
+    string inputFileName;
+    string outputFileName;
+    string depFileName;
+
+    vector<string> filesToPreprocess;
+};
+
+// takes the inputs from the command line and fills in the Options struct
+// Returns 0 on success, and nonzero on failure.
+// It also prints the usage statement on failure.
+int parse_options(int argc, const char* const* argv, Options *options);
+
+#endif // DEVICE_TOOLS_AIDL_H
diff --git a/tools/aidl/options_test.cpp b/tools/aidl/options_test.cpp
new file mode 100644
index 0000000..bd106ce
--- /dev/null
+++ b/tools/aidl/options_test.cpp
@@ -0,0 +1,291 @@
+#include <iostream>
+#include "options.h"
+
+const bool VERBOSE = false;
+
+using namespace std;
+
+struct Answer {
+    const char* argv[8];
+    int result;
+    const char* systemSearchPath[8];
+    const char* localSearchPath[8];
+    const char* inputFileName;
+    language_t nativeLanguage;
+    const char* outputH;
+    const char* outputCPP;
+    const char* outputJava;
+};
+
+bool
+match_arrays(const char* const*expected, const vector<string> &got)
+{
+    int count = 0;
+    while (expected[count] != NULL) {
+        count++;
+    }
+    if (got.size() != count) {
+        return false;
+    }
+    for (int i=0; i<count; i++) {
+        if (got[i] != expected[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+print_array(const char* prefix, const char* const*expected)
+{
+    while (*expected) {
+        cout << prefix << *expected << endl;
+        expected++;
+    }
+}
+
+void
+print_array(const char* prefix, const vector<string> &got)
+{
+    size_t count = got.size();
+    for (size_t i=0; i<count; i++) {
+        cout << prefix << got[i] << endl;
+    }
+}
+
+static int
+test(const Answer& answer)
+{
+    int argc = 0;
+    while (answer.argv[argc]) {
+        argc++;
+    }
+
+    int err = 0;
+
+    Options options;
+    int result = parse_options(argc, answer.argv, &options);
+
+    // result
+    if (((bool)result) != ((bool)answer.result)) {
+        cout << "mismatch: result: got " << result << " expected " <<
+            answer.result << endl;
+        err = 1;
+    }
+
+    if (result != 0) {
+        // if it failed, everything is invalid
+        return err;
+    }
+
+    // systemSearchPath
+    if (!match_arrays(answer.systemSearchPath, options.systemSearchPath)) {
+        cout << "mismatch: systemSearchPath: got" << endl;
+        print_array("        ", options.systemSearchPath);
+        cout << "    expected" << endl;
+        print_array("        ", answer.systemSearchPath);
+        err = 1;
+    }
+
+    // localSearchPath
+    if (!match_arrays(answer.localSearchPath, options.localSearchPath)) {
+        cout << "mismatch: localSearchPath: got" << endl;
+        print_array("        ", options.localSearchPath);
+        cout << "    expected" << endl;
+        print_array("        ", answer.localSearchPath);
+        err = 1;
+    }
+
+    // inputFileName
+    if (answer.inputFileName != options.inputFileName) {
+        cout << "mismatch: inputFileName: got " << options.inputFileName
+            << " expected " << answer.inputFileName << endl;
+        err = 1;
+    }
+
+    // nativeLanguage
+    if (answer.nativeLanguage != options.nativeLanguage) {
+        cout << "mismatch: nativeLanguage: got " << options.nativeLanguage
+            << " expected " << answer.nativeLanguage << endl;
+        err = 1;
+    }
+
+    // outputH
+    if (answer.outputH != options.outputH) {
+        cout << "mismatch: outputH: got " << options.outputH
+            << " expected " << answer.outputH << endl;
+        err = 1;
+    }
+
+    // outputCPP
+    if (answer.outputCPP != options.outputCPP) {
+        cout << "mismatch: outputCPP: got " << options.outputCPP
+            << " expected " << answer.outputCPP << endl;
+        err = 1;
+    }
+
+    // outputJava
+    if (answer.outputJava != options.outputJava) {
+        cout << "mismatch: outputJava: got " << options.outputJava
+            << " expected " << answer.outputJava << endl;
+        err = 1;
+    }
+
+    return err;
+}
+
+const Answer g_tests[] = {
+
+    {
+        /* argv */              { "test", "-i/moof", "-I/blah", "-Ibleh", "-imoo", "inputFileName.aidl_cpp", NULL, NULL },
+        /* result */            0,
+        /* systemSearchPath */  { "/blah", "bleh", NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { "/moof", "moo", NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "inputFileName.aidl_cpp",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "",
+        /* outputJava */        ""
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-oh", "outputH", NULL, NULL, NULL, NULL },
+        /* result */            0,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "inputFileName.aidl_cpp",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "outputH",
+        /* outputCPP */         "",
+        /* outputJava */        ""
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", NULL, NULL, NULL, NULL },
+        /* result */            0,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "inputFileName.aidl_cpp",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "outputCPP",
+        /* outputJava */        ""
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", NULL, NULL, NULL, NULL },
+        /* result */            0,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "inputFileName.aidl_cpp",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "",
+        /* outputJava */        "outputJava"
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-ocpp", "outputCPP", "-ojava", "outputJava" },
+        /* result */            0,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "inputFileName.aidl_cpp",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "outputH",
+        /* outputCPP */         "outputCPP",
+        /* outputJava */        "outputJava"
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-oh", "outputH1", NULL, NULL },
+        /* result */            1,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "",
+        /* outputJava */        ""
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", "-ocpp", "outputCPP1", NULL, NULL },
+        /* result */            1,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "",
+        /* outputJava */        ""
+    },
+
+    {
+        /* argv */              { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", "-ojava", "outputJava1", NULL, NULL },
+        /* result */            1,
+        /* systemSearchPath */  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* localSearchPath */   { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+        /* inputFileName */     "",
+        /* nativeLanguage */    CPP,
+        /* outputH */           "",
+        /* outputCPP */         "",
+        /* outputJava */        ""
+    },
+
+};
+
+int
+main(int argc, const char** argv)
+{
+    const int count = sizeof(g_tests)/sizeof(g_tests[0]);
+    int matches[count];
+
+    int result = 0;
+    for (int i=0; i<count; i++) {
+        if (VERBOSE) {
+            cout << endl;
+            cout << "---------------------------------------------" << endl;
+            const char* const* p = g_tests[i].argv;
+            while (*p) {
+                cout << " " << *p;
+                p++;
+            }
+            cout << endl;
+            cout << "---------------------------------------------" << endl;
+        }
+        matches[i] = test(g_tests[i]);
+        if (VERBOSE) {
+            if (0 == matches[i]) {
+                cout << "passed" << endl;
+            } else {
+                cout << "failed" << endl;
+            }
+            result |= matches[i];
+        }
+    }
+
+    cout << endl;
+    cout << "=============================================" << endl;
+    cout << "options_test summary" << endl;
+    cout << "=============================================" << endl;
+
+    if (!result) {
+        cout << "passed" << endl;
+    } else {
+        cout << "failed the following tests:" << endl;
+        for (int i=0; i<count; i++) {
+            if (matches[i]) {
+                cout << "   ";
+                const char* const* p = g_tests[i].argv;
+                while (*p) {
+                    cout << " " << *p;
+                    p++;
+                }
+                cout << endl;
+            }
+        }
+    }
+
+    return result;
+}
+
diff --git a/tools/aidl/search_path.cpp b/tools/aidl/search_path.cpp
new file mode 100644
index 0000000..ffb6cb2
--- /dev/null
+++ b/tools/aidl/search_path.cpp
@@ -0,0 +1,57 @@
+#include <unistd.h>
+#include "search_path.h"
+#include "options.h"
+#include <string.h>
+
+#ifdef HAVE_MS_C_RUNTIME
+#include <io.h>
+#endif
+
+static vector<string> g_importPaths;
+
+void
+set_import_paths(const vector<string>& importPaths)
+{
+    g_importPaths = importPaths;
+}
+
+char*
+find_import_file(const char* given)
+{
+    string expected = given;
+
+    int N = expected.length();
+    for (int i=0; i<N; i++) {
+        char c = expected[i];
+        if (c == '.') {
+            expected[i] = OS_PATH_SEPARATOR;
+        }
+    }
+    expected += ".aidl";
+
+    vector<string>& paths = g_importPaths;
+    for (vector<string>::iterator it=paths.begin(); it!=paths.end(); it++) {
+        string f = *it;
+        if (f.size() == 0) {
+            f = ".";
+            f += OS_PATH_SEPARATOR;
+        }
+        else if (f[f.size()-1] != OS_PATH_SEPARATOR) {
+            f += OS_PATH_SEPARATOR;
+        }
+        f.append(expected);
+
+#ifdef HAVE_MS_C_RUNTIME
+        /* check that the file exists and is not write-only */
+        if (0 == _access(f.c_str(), 0) &&  /* mode 0=exist */
+            0 == _access(f.c_str(), 4) ) { /* mode 4=readable */
+#else
+        if (0 == access(f.c_str(), R_OK)) {
+#endif        
+            return strdup(f.c_str());
+        }
+    }
+
+    return NULL;
+}
+
diff --git a/tools/aidl/search_path.h b/tools/aidl/search_path.h
new file mode 100644
index 0000000..2bf94b1
--- /dev/null
+++ b/tools/aidl/search_path.h
@@ -0,0 +1,23 @@
+#ifndef DEVICE_TOOLS_AIDL_SEARCH_PATH_H
+#define DEVICE_TOOLS_AIDL_SEARCH_PATH_H
+
+#include <stdio.h>
+
+#if __cplusplus
+#include <vector>
+#include <string>
+using namespace std;
+extern "C" {
+#endif
+
+// returns a FILE* and the char* for the file that it found
+// given is the class name we're looking for
+char* find_import_file(const char* given);
+
+#if __cplusplus
+}; // extern "C"
+void set_import_paths(const vector<string>& importPaths);
+#endif
+
+#endif // DEVICE_TOOLS_AIDL_SEARCH_PATH_H
+
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
new file mode 100644
index 0000000..6d606a9
--- /dev/null
+++ b/tools/layoutlib/Android.mk
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+#
+# Define rules to build temp_layoutlib.jar, which contains a subset of
+# the classes in framework.jar.  The layoutlib_create tool is used to
+# transform the framework jar into the temp_layoutlib jar.
+#
+
+# We need to process the framework classes.jar file, but we can't
+# depend directly on it (private vars won't be inherited correctly).
+# So, we depend on framework's BUILT file.
+built_framework_dep := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/javalib.jar
+built_framework_classes := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/classes.jar
+
+built_core_dep := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/javalib.jar
+built_core_classes := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar
+
+built_layoutlib_create_jar := $(call intermediates-dir-for, \
+			JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar
+
+# This is mostly a copy of config/host_java_library.mk
+LOCAL_MODULE := temp_layoutlib
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_BUILT_MODULE_STEM := javalib.jar
+
+#######################################
+include $(BUILD_SYSTEM)/base_rules.mk
+#######################################
+
+$(LOCAL_BUILT_MODULE): $(built_core_dep) \
+                       $(built_framework_dep) \
+                       $(built_layoutlib_create_jar)
+	@echo "host layoutlib_create: $@"
+	@mkdir -p $(dir $@)
+	@rm -f $@
+	$(hide) java -jar $(built_layoutlib_create_jar) \
+	             $@ \
+	             $(built_core_classes) \
+	             $(built_framework_classes)
+
+
+#
+# Include the subdir makefiles.
+#
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/layoutlib/api/.classpath b/tools/layoutlib/api/.classpath
new file mode 100644
index 0000000..a09ce5f
--- /dev/null
+++ b/tools/layoutlib/api/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/api/.project b/tools/layoutlib/api/.project
new file mode 100644
index 0000000..4e4ca3b
--- /dev/null
+++ b/tools/layoutlib/api/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_api</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/api/Android.mk b/tools/layoutlib/api/Android.mk
new file mode 100644
index 0000000..d60987c
--- /dev/null
+++ b/tools/layoutlib/api/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+	kxml2-2.3.0
+
+LOCAL_MODULE := layoutlib_api
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
new file mode 100644
index 0000000..df1876d
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+import java.util.Map;
+
+/**
+ * Entry point of the Layout Lib. Implementations of this interface provide a method to compute
+ * and render a layout.
+ * <p/>
+ * <p/>{@link #getApiLevel()} gives the ability to know which methods are available.
+ * <p/>
+ * Changes in API level 3:
+ * <ul>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li> deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
+ * Changes in API level 2:
+ * <ul>
+ * <li>{@link #getApiLevel()}</li>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
+ */
+public interface ILayoutBridge {
+    
+    final int API_CURRENT = 3;
+
+    /**
+     * Returns the API level of the layout library.
+     * While no methods will ever be removed, some may become deprecated, and some new ones
+     * will appear.
+     * <p/>If calling this method throws an {@link AbstractMethodError}, then the API level
+     * should be considered to be 1. 
+     */
+    int getApiLevel();
+
+    /**
+     * Initializes the Bridge object.
+     * @param fontOsLocation the location of the fonts.
+     * @param enumValueMap map attrName => { map enumFlagName => Integer value }. 
+     * @return true if success.
+     * @since 1
+     */
+    boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth the screen width
+     * @param screenHeight the screen height
+     * @param density the density factor for the screen.
+     * @param xdpi the screen actual dpi in X
+     * @param ydpi the screen actual dpi in Y
+     * @param themeName The name of the theme to use.
+     * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+     * @param projectResources the resources of the project. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the
+     * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+     * and the value is the resource value.
+     * @param frameworkResources the framework resources. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the map
+     * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+     * value is the resource value.
+     * @param projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @since 3
+     */
+    ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+            Object projectKey,
+            int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
+            String themeName, boolean isProjectTheme,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback projectCallback, ILayoutLog logger);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth the screen width
+     * @param screenHeight the screen height
+     * @param themeName The name of the theme to use.
+     * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+     * @param projectResources the resources of the project. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the
+     * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+     * and the value is the resource value.
+     * @param frameworkResources the framework resources. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the map
+     * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+     * value is the resource value.
+     * @param projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
+     * @since 2
+     */
+    @Deprecated
+    ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+            Object projectKey,
+            int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback projectCallback, ILayoutLog logger);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth
+     * @param screenHeight
+     * @param themeName The name of the theme to use. In order to differentiate project and platform
+     * themes sharing the same name, all project themes must be prepended with a '*' character.
+     * @param projectResources the resources of the project. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the
+     * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+     * and the value is the resource value.
+     * @param frameworkResources the framework resources. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the map
+     * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+     * value is the resource value.
+     * @param projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
+     * @since 1
+     */
+    @Deprecated
+    ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+            Object projectKey,
+            int screenWidth, int screenHeight, String themeName,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback projectCallback, ILayoutLog logger);
+    
+    /**
+     * Clears the resource cache for a specific project.
+     * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused
+     * until this method is called.
+     * <p/>The cache is not configuration dependent and should only be cleared when a
+     * resource changes (at this time only bitmaps and 9 patches go into the cache).
+     * @param objectKey the key for the project.
+     * @since 1
+     */
+    void clearCaches(Object projectKey);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java
new file mode 100644
index 0000000..cae15d3
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+/**
+ * Callback interface to display warnings/errors that happened during the computation and
+ * rendering of the layout. 
+ */
+public interface ILayoutLog {
+    
+    /**
+     * Displays a warning message.
+     * @param message the message to display.
+     */
+    void warning(String message);
+    
+    /**
+     * Displays an error message.
+     * @param message the message to display.
+     */
+    void error(String message);
+    
+    /**
+     * Displays an exception
+     * @param t the {@link Throwable} to display.
+     */
+    void error(Throwable t);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java
new file mode 100644
index 0000000..5a06349
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * The result of a layout computation through
+ * {@link ILayoutLibBridge#computeLayout(IXmlPullParser, int, int, String, java.util.Map, java.util.Map, java.util.Map, IFontLoader, ILayoutLibLog, ICustomViewLoader)}
+ */
+public interface ILayoutResult {
+    /** Sucess return code */
+    final static int SUCCESS = 0;
+    /** Error return code.
+     *  <p/>See {@link #getErrorMessage()}
+     */ 
+    final static int ERROR = 1;
+    
+    /**
+     * Returns the result code.
+     * @see #SUCCESS
+     * @see #ERROR
+     */
+    int getSuccess();
+
+    /**
+     * Returns the {@link ILayoutViewInfo} object for the top level view.
+     */
+    ILayoutViewInfo getRootView();
+
+    /**
+     * Returns the rendering of the full layout.
+     */
+    BufferedImage getImage();
+
+    /**
+     * Returns the error message.
+     * <p/>Only valid when {@link #getSuccess()} returns {@link #ERROR}
+     */
+    String getErrorMessage();
+
+    /**
+     * Layout information for a specific view.
+     */
+    public interface ILayoutViewInfo {
+
+        /**
+         * Returns the list of children views.
+         */
+        ILayoutViewInfo[] getChildren();
+        
+        /**
+         * Returns the key associated with the node.
+         * @see IXmlPullParser#getViewKey()
+         */
+        Object getViewKey();
+        
+        /**
+         * Returns the name of the view.
+         */
+        String getName();
+        
+        /**
+         * Returns the left of the view bounds.
+         */
+        int getLeft();
+
+        /**
+         * Returns the top of the view bounds.
+         */
+        int getTop();
+
+        /**
+         * Returns the right of the view bounds.
+         */
+        int getRight();
+
+        /**
+         * Returns the bottom of the view bounds.
+         */
+        int getBottom();
+    }
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java
new file mode 100644
index 0000000..5ad5082
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+/**
+ * Callback for project information needed by the Layout Library.
+ * Classes implementing this interface provide methods giving access to some project data, like
+ * resource resolution, namespace information, and instantiation of custom view.
+ */
+public interface IProjectCallback {
+    
+    /**
+     * Loads a custom view with the given constructor signature and arguments.
+     * @param name The fully qualified name of the class.
+     * @param constructorSignature The signature of the class to use
+     * @param constructorArgs The arguments to use on the constructor
+     * @return A newly instantiated android.view.View object.
+     * @throws ClassNotFoundException.
+     * @throws Exception 
+     */
+    @SuppressWarnings("unchecked")
+    Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+        throws ClassNotFoundException, Exception;
+    
+    /**
+     * Returns the namespace of the application.
+     * <p/>This lets the Layout Lib load custom attributes for custom views.
+     */
+    String getNamespace();
+    
+    /**
+     * Resolves the id of a resource Id.
+     * <p/>The resource id is the value of a <code>R.&lt;type&gt;.&lt;name&gt;</code>, and
+     * this method will return both the type and name of the resource.
+     * @param id the Id to resolve.
+     * @return an array of 2 strings containing the resource name and type, or null if the id
+     * does not match any resource. 
+     */
+    String[] resolveResourceValue(int id);
+    
+    /**
+     * Resolves the id of a resource Id of type int[]
+     * <p/>The resource id is the value of a R.styleable.&lt;name&gt;, and this method will
+     * return the name of the resource.
+     * @param id the Id to resolve.
+     * @return the name of the resource or <code>null</code> if not found.
+     */
+    String resolveResourceValue(int[] id);
+    
+    /**
+     * Returns the id of a resource.
+     * <p/>The provided type and name must match an existing constant defined as
+     * <code>R.&lt;type&gt;.&lt;name&gt;</code>.
+     * @param type the type of the resource
+     * @param name the name of the resource
+     * @return an Integer containing the resource Id, or <code>null</code> if not found.
+     */
+    Integer getResourceValue(String type, String name);
+
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java
new file mode 100644
index 0000000..1da9508
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+/**
+ * Represents an android resource with a name and a string value.
+ */
+public interface IResourceValue {
+    
+    /**
+     * Returns the type of the resource. For instance "drawable", "color", etc...
+     */
+    String getType();
+
+    /**
+     * Returns the name of the resource, as defined in the XML.
+     */
+    String getName();
+
+    /**
+     * Returns the value of the resource, as defined in the XML. This can be <code>null</code>
+     */
+    String getValue();
+    
+    /**
+     * Returns whether the resource is a framework resource (<code>true</code>) or a project
+     * resource (<code>false</false>).
+     */
+    boolean isFramework();
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java
new file mode 100644
index 0000000..2f17e69
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+
+/**
+ * Represents an android style resources with a name and a list of children {@link IResourceValue}.
+ */
+public interface IStyleResourceValue extends IResourceValue {
+    
+    /**
+     * Returns the parent style name or <code>null</code> if unknown.
+     */
+    String getParentStyle();
+
+    /**
+     * Find an item in the list by name
+     * @param name
+     */
+    IResourceValue findItem(String name);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java
new file mode 100644
index 0000000..cd43c56
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.api;
+
+import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Extended version of {@link XmlPullParser} to use with 
+ * {@link ILayoutLibBridge#computeLayout(XmlPullParser, int, int, String, java.util.Map, java.util.Map, java.util.Map, com.android.layoutlib.api.ILayoutLibBridge.IFontInfo)}
+ */
+public interface IXmlPullParser extends XmlPullParser {
+    
+    /**
+     * Returns a key for the current XML node.
+     * <p/>This key will be passed back in the {@link ILayoutViewInfo} objects, allowing association
+     * of a particular XML node with its result from the layout computation.
+     */
+    Object getViewKey();
+}
+
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
new file mode 100644
index 0000000..175a98b
--- /dev/null
+++ b/tools/layoutlib/bridge/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
+	<classpathentry kind="src" path="tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
+	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
+	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/bridge/.project b/tools/layoutlib/bridge/.project
new file mode 100644
index 0000000..e36e71b
--- /dev/null
+++ b/tools/layoutlib/bridge/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_bridge</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
new file mode 100644
index 0000000..b2010d5
--- /dev/null
+++ b/tools/layoutlib/bridge/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+	kxml2-2.3.0 \
+	layoutlib_api \
+	ninepatch
+
+LOCAL_STATIC_JAVA_LIBRARIES := temp_layoutlib
+
+LOCAL_MODULE := layoutlib
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
new file mode 100644
index 0000000..6bc01b1
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.BridgeCanvas;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+public final class Bitmap extends _Original_Bitmap {
+    
+    private BufferedImage mImage;
+
+    public Bitmap(File input) throws IOException {
+        super(1, true, null);
+        
+        mImage = ImageIO.read(input);
+    }
+    
+    Bitmap(BufferedImage image) {
+        super(1, true, null);
+        mImage = image;
+    }
+
+    public BufferedImage getImage() {
+        return mImage;
+    }
+    
+    // ----- overriden methods
+    
+    public enum Config {
+        // these native values must match up with the enum in SkBitmap.h
+        ALPHA_8     (2),
+        RGB_565     (4),
+        ARGB_4444   (5),
+        ARGB_8888   (6);
+
+        Config(int ni) {
+            this.nativeInt = ni;
+        }
+        final int nativeInt;
+        
+        /* package */ static Config nativeToConfig(int ni) {
+            return sConfigs[ni];
+        }
+        
+        private static Config sConfigs[] = {
+            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
+        };
+    }
+
+    
+    @Override
+    public int getWidth() {
+        return mImage.getWidth();
+    }
+    
+    @Override
+    public int getHeight() {
+        return mImage.getHeight();
+    }
+    
+    /**
+     * Returns an immutable bitmap from the source bitmap. The new bitmap may
+     * be the same object as source, or a copy may have been made.
+     */
+    public static Bitmap createBitmap(Bitmap src) {
+        return createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), null, false);
+    }
+
+    /**
+     * Returns an immutable bitmap from the specified subset of the source
+     * bitmap. The new bitmap may be the same object as source, or a copy may
+     * have been made.
+     *
+     * @param source   The bitmap we are subsetting
+     * @param x        The x coordinate of the first pixel in source
+     * @param y        The y coordinate of the first pixel in source
+     * @param width    The number of pixels in each row
+     * @param height   The number of rows
+     */
+    public static Bitmap createBitmap(Bitmap source, int x, int y,
+                                      int width, int height) {
+        return new Bitmap(source.mImage.getSubimage(x, y, width, height));
+    }
+    
+    /**
+     * Returns an immutable bitmap from subset of the source bitmap,
+     * transformed by the optional matrix.
+     *
+     * @param source   The bitmap we are subsetting
+     * @param x        The x coordinate of the first pixel in source
+     * @param y        The y coordinate of the first pixel in source
+     * @param width    The number of pixels in each row
+     * @param height   The number of rows
+     * @param m        Option matrix to be applied to the pixels
+     * @param filter   true if the source should be filtered.
+     *                   Only applies if the matrix contains more than just
+     *                   translation.
+     * @return A bitmap that represents the specified subset of source
+     * @throws IllegalArgumentException if the x, y, width, height values are
+     *         outside of the dimensions of the source bitmap.
+     */
+    public static Bitmap createBitmap(Bitmap source, int x, int y, int width,
+                                      int height, Matrix m, boolean filter) {
+        checkXYSign(x, y);
+        checkWidthHeight(width, height);
+        if (x + width > source.getWidth()) {
+            throw new IllegalArgumentException(
+                    "x + width must be <= bitmap.width()");
+        }
+        if (y + height > source.getHeight()) {
+            throw new IllegalArgumentException(
+                    "y + height must be <= bitmap.height()");
+        }
+
+        // check if we can just return our argument unchanged
+        if (!source.isMutable() && x == 0 && y == 0
+                && width == source.getWidth() && height == source.getHeight()
+                && (m == null || m.isIdentity())) {
+            return source;
+        }
+
+        if (m == null || m.isIdentity()) {
+            return new Bitmap(source.mImage.getSubimage(x, y, width, height));
+        }
+
+        int neww = width;
+        int newh = height;
+        Paint paint;
+
+        Rect srcR = new Rect(x, y, x + width, y + height);
+        RectF dstR = new RectF(0, 0, width, height);
+
+        /*  the dst should have alpha if the src does, or if our matrix
+            doesn't preserve rectness
+        */
+        boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
+        RectF deviceR = new RectF();
+        m.mapRect(deviceR, dstR);
+        neww = Math.round(deviceR.width());
+        newh = Math.round(deviceR.height());
+
+        BridgeCanvas canvas = new BridgeCanvas(neww, newh);
+
+        canvas.translate(-deviceR.left, -deviceR.top);
+        canvas.concat(m);
+        paint = new Paint();
+        paint.setFilterBitmap(filter);
+        if (!m.rectStaysRect()) {
+            paint.setAntiAlias(true);
+        }
+
+        canvas.drawBitmap(source, srcR, dstR, paint);
+        
+        return new Bitmap(canvas.getImage());
+    }
+    
+    /**
+     * Returns a mutable bitmap with the specified width and height.
+     *
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create.
+     * @throws IllegalArgumentException if the width or height are <= 0
+     */
+    public static Bitmap createBitmap(int width, int height, Config config) {
+        return new Bitmap(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB));
+    }
+    
+    /**
+     * Returns a immutable bitmap with the specified width and height, with each
+     * pixel value set to the corresponding value in the colors array.
+     *
+     * @param colors   Array of {@link Color} used to initialize the pixels.
+     * @param offset   Number of values to skip before the first color in the
+     *                 array of colors.
+     * @param stride   Number of colors in the array between rows (must be >=
+     *                 width or <= -width).
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create. If the config does not
+     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
+     *                 bytes in the colors[] will be ignored (assumed to be FF)
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         the color array's length is less than the number of pixels.
+     */
+    public static Bitmap createBitmap(int colors[], int offset, int stride,
+                                      int width, int height, Config config) {
+        checkWidthHeight(width, height);
+        if (Math.abs(stride) < width) {
+            throw new IllegalArgumentException("abs(stride) must be >= width");
+        }
+        int lastScanline = offset + (height - 1) * stride;
+        int length = colors.length;
+        if (offset < 0 || (offset + width > length)
+            || lastScanline < 0
+            || (lastScanline + width > length)) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        
+        // TODO: create an immutable bitmap...
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a immutable bitmap with the specified width and height, with each
+     * pixel value set to the corresponding value in the colors array.
+     *
+     * @param colors   Array of {@link Color} used to initialize the pixels.
+     *                 This array must be at least as large as width * height.
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create. If the config does not
+     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
+     *                 bytes in the colors[] will be ignored (assumed to be FF)
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         the color array's length is less than the number of pixels.
+     */
+    public static Bitmap createBitmap(int colors[], int width, int height,
+                                      Config config) {
+        return createBitmap(colors, 0, width, width, height, config);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
new file mode 100644
index 0000000..8bf7fcc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+public class BitmapShader extends Shader {
+
+    // we hold on just for the GC, since our native counterpart is using it
+    private final Bitmap mBitmap;
+
+    /**
+     * Call this to create a new shader that will draw with a bitmap.
+     *
+     * @param bitmap            The bitmap to use inside the shader
+     * @param tileX             The tiling mode for x to draw the bitmap in.
+     * @param tileY             The tiling mode for y to draw the bitmap in.
+     */
+    public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
+        mBitmap = bitmap;
+    }
+    
+    //---------- Custom methods
+    
+    public Bitmap getBitmap() {
+        return mBitmap;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
new file mode 100644
index 0000000..968a597
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+/** A subclass of shader that returns the composition of two other shaders, combined by
+    an {@link android.graphics.Xfermode} subclass.
+*/
+public class ComposeShader extends Shader {
+    /** Create a new compose shader, given shaders A, B, and a combining mode.
+        When the mode is applied, it will be given the result from shader A as its
+        "dst", and the result of from shader B as its "src".
+        @param shaderA  The colors from this shader are seen as the "dst" by the mode
+        @param shaderB  The colors from this shader are seen as the "src" by the mode
+        @param mode     The mode that combines the colors from the two shaders. If mode
+                        is null, then SRC_OVER is assumed.
+    */
+    public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
+        // FIXME Implement shader
+    }
+
+    /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
+        When the mode is applied, it will be given the result from shader A as its
+        "dst", and the result of from shader B as its "src".
+        @param shaderA  The colors from this shader are seen as the "dst" by the mode
+        @param shaderB  The colors from this shader are seen as the "src" by the mode
+        @param mode     The PorterDuff mode that combines the colors from the two shaders.
+    */
+    public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
+        // FIXME Implement shader
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
new file mode 100644
index 0000000..1a0dc05
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.GradientPaint;
+import java.awt.Color;
+import java.awt.Paint;
+
+public class LinearGradient extends Shader {
+    
+    private GradientPaint mGradientPaint;
+
+    /** Create a shader that draws a linear gradient along a line.
+        @param x0           The x-coordinate for the start of the gradient line
+        @param y0           The y-coordinate for the start of the gradient line
+        @param x1           The x-coordinate for the end of the gradient line
+        @param y1           The y-coordinate for the end of the gradient line
+        @param  colors      The colors to be distributed along the gradient line
+        @param  positions   May be null. The relative positions [0..1] of
+                            each corresponding color in the colors array. If this is null,
+                            the the colors are distributed evenly along the gradient line.
+        @param  tile        The Shader tiling mode
+    */
+    public LinearGradient(float x0, float y0, float x1, float y1,
+                          int colors[], float positions[], TileMode tile) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+        
+        // FIXME implement multi color linear gradient
+    }
+
+    /** Create a shader that draws a linear gradient along a line.
+        @param x0       The x-coordinate for the start of the gradient line
+        @param y0       The y-coordinate for the start of the gradient line
+        @param x1       The x-coordinate for the end of the gradient line
+        @param y1       The y-coordinate for the end of the gradient line
+        @param  color0  The color at the start of the gradient line.
+        @param  color1  The color at the end of the gradient line.
+        @param  tile    The Shader tiling mode
+    */
+    public LinearGradient(float x0, float y0, float x1, float y1,
+                          int color0, int color1, TileMode tile) {
+        mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */),
+                x1,y1, new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
+    }
+    
+    //---------- Custom Methods
+    
+    public Paint getPaint() {
+        return mGradientPaint;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
new file mode 100644
index 0000000..3f9a993
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -0,0 +1,984 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.geom.AffineTransform;
+
+
+/**
+ * A matrix implementation overridden by the LayoutLib bridge.
+ */
+public class Matrix extends _Original_Matrix {
+
+    float mValues[] = new float[9]; 
+    
+    /**
+     * Create an identity matrix
+     */
+    public Matrix() {
+        reset();
+    }
+
+    /**
+     * Create a matrix that is a (deep) copy of src
+     * @param src The matrix to copy into this matrix
+     */
+    public Matrix(Matrix src) {
+        set(src);
+    }
+    
+    /**
+     * Creates a Matrix object from the float array. The array becomes the internal storage
+     * of the object.
+     * @param data
+     */
+    private Matrix(float[] data) {
+        assert data.length != 9;
+        mValues = data;
+    }
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    //---------- Custom Methods
+    
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = this*matrix
+     * @param matrix
+     */
+    private void addTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        
+        // first row 
+        tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
+        tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
+        tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
+        
+        // 2nd row
+        tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
+        tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
+        tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
+        
+        // 3rd row
+        tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
+        tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
+        tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
+
+        // copy the result over to mValues
+        mValues = tmp;
+    }
+    
+    public AffineTransform getTransform() {
+        return new AffineTransform(mValues[0], mValues[1], mValues[2],
+                mValues[3], mValues[4], mValues[5]);
+    }
+    
+    public boolean hasPerspective() {
+        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
+    }
+    
+    //----------
+
+    /**
+     * Returns true if the matrix is identity.
+     * This maybe faster than testing if (getType() == 0)
+     */
+    @Override
+    public boolean isIdentity() {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                if (mValues[k] != ((i==j) ? 1 : 0)) {
+                    return false;
+                }
+            }
+        }
+        
+        return true;
+    }
+
+    /**
+     * Returns true if will map a rectangle to another rectangle. This can be
+     * true if the matrix is identity, scale-only, or rotates a multiple of 90
+     * degrees.
+     */
+    @Override
+    public boolean rectStaysRect() {
+        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+    
+    /**
+     * (deep) copy the src matrix into this matrix. If src is null, reset this
+     * matrix to the identity matrix.
+     */
+    public void set(Matrix src) {
+        if (src == null) {
+            reset();
+        } else {
+            System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
+        }
+    }
+
+    @Override
+    public void set(_Original_Matrix src) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    /** Returns true if obj is a Matrix and its values equal our values.
+    */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj != null && obj instanceof Matrix) {
+            Matrix matrix = (Matrix)obj;
+            for (int i = 0 ; i < 9 ; i++) {
+                if (mValues[i] != matrix.mValues[i]) {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        
+        return false;
+    }
+
+    /** Set the matrix to identity */
+    @Override
+    public void reset() {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                mValues[k] = ((i==j) ? 1 : 0);
+            }
+        }
+    }
+
+    /** Set the matrix to translate by (dx, dy). */
+    @Override
+    public void setTranslate(float dx, float dy) {
+        mValues[0] = 1;
+        mValues[1] = 0;
+        mValues[2] = dx;
+        mValues[3] = 0;
+        mValues[4] = 1;
+        mValues[5] = dy;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+    }
+
+    /**
+     * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
+     * The pivot point is the coordinate that should remain unchanged by the
+     * specified transformation.
+     */
+    @Override
+    public void setScale(float sx, float sy, float px, float py) {
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        mValues[0] = 1;
+        mValues[1] = 0;
+        mValues[2] = -px;
+        mValues[3] = 0;
+        mValues[4] = 1;
+        mValues[5] = -py;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+
+        // scale
+        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+    }
+
+    /** Set the matrix to scale by sx and sy. */
+    @Override
+    public void setScale(float sx, float sy) {
+        mValues[0] = sx;
+        mValues[1] = 0;
+        mValues[2] = 0;
+        mValues[3] = 0;
+        mValues[4] = sy;
+        mValues[5] = 0;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+    }
+
+    /**
+     * Set the matrix to rotate by the specified number of degrees, with a pivot
+     * point at (px, py). The pivot point is the coordinate that should remain
+     * unchanged by the specified transformation.
+     */
+    @Override
+    public void setRotate(float degrees, float px, float py) {
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        mValues[0] = 1;
+        mValues[1] = 0;
+        mValues[2] = -px;
+        mValues[3] = 0;
+        mValues[4] = 1;
+        mValues[5] = -py;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+
+        // scale
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); 
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+    }
+
+    /**
+     * Set the matrix to rotate about (0,0) by the specified number of degrees.
+     */
+    @Override
+    public void setRotate(float degrees) {
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+
+        mValues[0] = cos;
+        mValues[1] = -sin;
+        mValues[2] = 0;
+        mValues[3] = sin;
+        mValues[4] = cos;
+        mValues[5] = 0;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+    }
+
+    /**
+     * Set the matrix to rotate by the specified sine and cosine values, with a
+     * pivot point at (px, py). The pivot point is the coordinate that should
+     * remain unchanged by the specified transformation.
+     */
+    @Override
+    public void setSinCos(float sinValue, float cosValue, float px, float py) {
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        mValues[0] = 1;
+        mValues[1] = 0;
+        mValues[2] = -px;
+        mValues[3] = 0;
+        mValues[4] = 1;
+        mValues[5] = -py;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+
+        // scale
+        addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 }); 
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+    }
+
+    /** Set the matrix to rotate by the specified sine and cosine values. */
+    @Override
+    public void setSinCos(float sinValue, float cosValue) {
+        mValues[0] = cosValue;
+        mValues[1] = -sinValue;
+        mValues[2] = 0;
+        mValues[3] = sinValue;
+        mValues[4] = cosValue;
+        mValues[5] = 0;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+    }
+
+    /**
+     * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
+     * The pivot point is the coordinate that should remain unchanged by the
+     * specified transformation.
+     */
+    @Override
+    public void setSkew(float kx, float ky, float px, float py) {
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        mValues[0] = 1;
+        mValues[1] = 0;
+        mValues[2] = -px;
+        mValues[3] = 0;
+        mValues[4] = 1;
+        mValues[5] = -py;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+
+        // scale
+        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+    }
+
+    /** Set the matrix to skew by sx and sy. */
+    @Override
+    public void setSkew(float kx, float ky) {
+        mValues[0] = 1;
+        mValues[1] = kx;
+        mValues[2] = -0;
+        mValues[3] = ky;
+        mValues[4] = 1;
+        mValues[5] = 0;
+        mValues[6] = 0;
+        mValues[7] = 0;
+        mValues[7] = 1;
+    }
+
+    /**
+     * Set the matrix to the concatenation of the two specified matrices,
+     * returning true if the the result can be represented. Either of the two
+     * matrices may also be the target matrix. this = a * b
+     */
+    public boolean setConcat(Matrix a, Matrix b) {
+        if (a == this) {
+            preConcat(b);
+        } else if (b == this) {
+            postConcat(b);
+        } else {
+            Matrix tmp = new Matrix(b);
+            tmp.addTransform(a.mValues);
+            set(tmp);
+        }
+        
+        return true;
+    }
+    
+    @Override
+    public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    /**
+     * Preconcats the matrix with the specified translation.
+     * M' = M * T(dx, dy)
+     */
+    @Override
+    public boolean preTranslate(float dx, float dy) {
+        // create a matrix that will be multiply by this
+        Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
+        m.addTransform(this.mValues);
+        
+        System.arraycopy(m.mValues, 0, mValues, 0, 9);
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified scale.
+     * M' = M * S(sx, sy, px, py)
+     */
+    @Override
+    public boolean preScale(float sx, float sy, float px, float py) {
+        Matrix m = new Matrix();
+        m.setScale(sx, sy, px, py);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified scale.
+     * M' = M * S(sx, sy)
+     */
+    @Override
+    public boolean preScale(float sx, float sy) {
+        Matrix m = new Matrix();
+        m.setScale(sx, sy);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified rotation.
+     * M' = M * R(degrees, px, py)
+     */
+    @Override
+    public boolean preRotate(float degrees, float px, float py) {
+        Matrix m = new Matrix();
+        m.setRotate(degrees, px, py);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified rotation.
+     * M' = M * R(degrees)
+     */
+    @Override
+    public boolean preRotate(float degrees) {
+        Matrix m = new Matrix();
+        m.setRotate(degrees);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified skew.
+     * M' = M * K(kx, ky, px, py)
+     */
+    @Override
+    public boolean preSkew(float kx, float ky, float px, float py) {
+        Matrix m = new Matrix();
+        m.setSkew(kx, ky, px, py);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified skew.
+     * M' = M * K(kx, ky)
+     */
+    @Override
+    public boolean preSkew(float kx, float ky) {
+        Matrix m = new Matrix();
+        m.setSkew(kx, ky);
+        m.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix with the specified matrix.
+     * M' = M * other
+     */
+    public boolean preConcat(Matrix other) {
+        Matrix m = new Matrix(other);
+        other.addTransform(mValues);
+        set(m);
+        
+        return true;
+    }
+    
+    @Override
+    public boolean preConcat(_Original_Matrix other) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    /**
+     * Postconcats the matrix with the specified translation.
+     * M' = T(dx, dy) * M
+     */
+    @Override
+    public boolean postTranslate(float dx, float dy) {
+        addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified scale.
+     * M' = S(sx, sy, px, py) * M
+     */
+    @Override
+    public boolean postScale(float sx, float sy, float px, float py) {
+        // TODO: do it in one pass
+        // translate so that the pivot is in 0,0
+        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
+        // scale
+        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+        
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified scale.
+     * M' = S(sx, sy) * M
+     */
+    @Override
+    public boolean postScale(float sx, float sy) {
+        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified rotation.
+     * M' = R(degrees, px, py) * M
+     */
+    @Override
+    public boolean postRotate(float degrees, float px, float py) {
+        // TODO: do it in one pass
+        // translate so that the pivot is in 0,0
+        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
+        // scale
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); 
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+        
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified rotation.
+     * M' = R(degrees) * M
+     */
+    @Override
+    public boolean postRotate(float degrees) {
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
+        
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified skew.
+     * M' = K(kx, ky, px, py) * M
+     */
+    @Override
+    public boolean postSkew(float kx, float ky, float px, float py) {
+        // TODO: do it in one pass
+        // translate so that the pivot is in 0,0
+        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
+        // scale
+        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        // translate back the pivot
+        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
+        
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified skew.
+     * M' = K(kx, ky) * M
+     */
+    @Override
+    public boolean postSkew(float kx, float ky) {
+        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        
+        return true;
+    }
+
+    /**
+     * Postconcats the matrix with the specified matrix.
+     * M' = other * M
+     */
+    public boolean postConcat(Matrix other) {
+        addTransform(other.mValues);
+        
+        return true;
+    }
+
+    @Override
+    public boolean postConcat(_Original_Matrix other) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+    
+    /** Controlls how the src rect should align into the dst rect for
+        setRectToRect().
+    */
+    public enum ScaleToFit {
+        /**
+         * Scale in X and Y independently, so that src matches dst exactly.
+         * This may change the aspect ratio of the src.
+         */
+        FILL    (0),
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. START aligns the result to the
+         * left and top edges of dst.
+         */
+        START   (1),
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. The result is centered inside dst.
+         */
+        CENTER  (2),
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. END aligns the result to the
+         * right and bottom edges of dst.
+         */
+        END     (3);
+
+        // the native values must match those in SkMatrix.h 
+        ScaleToFit(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    /**
+     * Set the matrix to the scale and translate values that map the source
+     * rectangle to the destination rectangle, returning true if the result
+     * can be represented.
+     *
+     * @param src the source rectangle to map from.
+     * @param dst the destination rectangle to map to.
+     * @param stf the ScaleToFit option
+     * @return true if the matrix can be represented by the rectangle mapping.
+     */
+    public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
+        if (dst == null || src == null) {
+            throw new NullPointerException();
+        }
+        
+        if (src.isEmpty()) {
+            reset();
+            return false;
+        }
+
+        if (dst.isEmpty()) {
+            mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
+               = mValues[6] = mValues[7] = 0;
+            mValues[8] = 1;
+        } else {
+            float    tx, sx = dst.width() / src.width();
+            float    ty, sy = dst.height() / src.height();
+            boolean  xLarger = false;
+
+            if (stf != ScaleToFit.FILL) {
+                if (sx > sy) {
+                    xLarger = true;
+                    sx = sy;
+                } else {
+                    sy = sx;
+                }
+            }
+
+            tx = dst.left - src.left * sx;
+            ty = dst.top - src.top * sy;
+            if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
+                float diff;
+
+                if (xLarger) {
+                    diff = dst.width() - src.width() * sy;
+                } else {
+                    diff = dst.height() - src.height() * sy;
+                }
+                
+                if (stf == ScaleToFit.CENTER) {
+                    diff = diff / 2;
+                }
+
+                if (xLarger) {
+                    tx += diff;
+                } else {
+                    ty += diff;
+                }
+            }
+
+            mValues[0] = sx;
+            mValues[4] = sy;
+            mValues[2] = tx;
+            mValues[5] = ty;
+            mValues[1]  = mValues[3] = mValues[6] = mValues[7] = 0;
+
+        }
+        // shared cleanup
+        mValues[8] = 1;
+        return true;
+    }
+    
+    @Override
+    public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+    
+    /**
+     * Set the matrix such that the specified src points would map to the
+     * specified dst points. The "points" are represented as an array of floats,
+     * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
+     *
+     * @param src   The array of src [x,y] pairs (points)
+     * @param srcIndex Index of the first pair of src values
+     * @param dst   The array of dst [x,y] pairs (points)
+     * @param dstIndex Index of the first pair of dst values
+     * @param pointCount The number of pairs/points to be used. Must be [0..4]
+     * @return true if the matrix was set to the specified transformation
+     */
+    @Override
+    public boolean setPolyToPoly(float[] src, int srcIndex,
+                                 float[] dst, int dstIndex,
+                                 int pointCount) {
+        if (pointCount > 4) {
+            throw new IllegalArgumentException();
+        }
+        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+
+    /**
+     * If this matrix can be inverted, return true and if inverse is not null,
+     * set inverse to be the inverse of this matrix. If this matrix cannot be
+     * inverted, ignore inverse and return false.
+     */
+    public boolean invert(Matrix inverse) {
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    @Override
+    public boolean invert(_Original_Matrix inverse) {
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    /**
+    * Apply this matrix to the array of 2D points specified by src, and write
+     * the transformed points into the array of points specified by dst. The
+     * two arrays represent their "points" as pairs of floats [x, y].
+     *
+     * @param dst   The array of dst points (x,y pairs)
+     * @param dstIndex The index of the first [x,y] pair of dst floats
+     * @param src   The array of src points (x,y pairs)
+     * @param srcIndex The index of the first [x,y] pair of src floats
+     * @param pointCount The number of points (x,y pairs) to transform
+     */
+    @Override
+    public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
+                          int pointCount) {
+        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    /**
+    * Apply this matrix to the array of 2D vectors specified by src, and write
+     * the transformed vectors into the array of vectors specified by dst. The
+     * two arrays represent their "vectors" as pairs of floats [x, y].
+     *
+     * @param dst   The array of dst vectors (x,y pairs)
+     * @param dstIndex The index of the first [x,y] pair of dst floats
+     * @param src   The array of src vectors (x,y pairs)
+     * @param srcIndex The index of the first [x,y] pair of src floats
+     * @param vectorCount The number of vectors (x,y pairs) to transform
+     */
+    @Override
+    public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
+                          int vectorCount) {
+        checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    /**
+     * Apply this matrix to the array of 2D points specified by src, and write
+     * the transformed points into the array of points specified by dst. The
+     * two arrays represent their "points" as pairs of floats [x, y].
+     *
+     * @param dst   The array of dst points (x,y pairs)
+     * @param src   The array of src points (x,y pairs)
+     */
+    @Override
+    public void mapPoints(float[] dst, float[] src) {
+        if (dst.length != src.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mapPoints(dst, 0, src, 0, dst.length >> 1);
+    }
+
+    /**
+     * Apply this matrix to the array of 2D vectors specified by src, and write
+     * the transformed vectors into the array of vectors specified by dst. The
+     * two arrays represent their "vectors" as pairs of floats [x, y].
+     *
+     * @param dst   The array of dst vectors (x,y pairs)
+     * @param src   The array of src vectors (x,y pairs)
+     */
+    @Override
+    public void mapVectors(float[] dst, float[] src) {
+        if (dst.length != src.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mapVectors(dst, 0, src, 0, dst.length >> 1);
+    }
+
+    /**
+     * Apply this matrix to the array of 2D points, and write the transformed
+     * points back into the array
+     *
+     * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
+     */
+    @Override
+    public void mapPoints(float[] pts) {
+        mapPoints(pts, 0, pts, 0, pts.length >> 1);
+    }
+
+    /**
+     * Apply this matrix to the array of 2D vectors, and write the transformed
+     * vectors back into the array.
+     * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
+     */
+    @Override
+    public void mapVectors(float[] vecs) {
+        mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
+    }
+
+    /**
+     * Apply this matrix to the src rectangle, and write the transformed
+     * rectangle into dst. This is accomplished by transforming the 4 corners of
+     * src, and then setting dst to the bounds of those points.
+     *
+     * @param dst Where the transformed rectangle is written.
+     * @param src The original rectangle to be transformed.
+     * @return the result of calling rectStaysRect()
+     */
+    @Override
+    public boolean mapRect(RectF dst, RectF src) {
+        if (dst == null || src == null) {
+            throw new NullPointerException();
+        }
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+
+    /**
+     * Apply this matrix to the rectangle, and write the transformed rectangle
+     * back into it. This is accomplished by transforming the 4 corners of rect,
+     * and then setting it to the bounds of those points
+     *
+     * @param rect The rectangle to transform.
+     * @return the result of calling rectStaysRect()
+     */
+    @Override
+    public boolean mapRect(RectF rect) {
+        return mapRect(rect, rect);
+    }
+
+    /**
+     * Return the mean radius of a circle after it has been mapped by
+     * this matrix. NOTE: in perspective this value assumes the circle
+     * has its center at the origin.
+     */
+    @Override
+    public float mapRadius(float radius) {
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    /** Copy 9 values from the matrix into the array.
+    */
+    @Override
+    public void getValues(float[] values) {
+        if (values.length < 9) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        System.arraycopy(mValues, 0, values, 0, mValues.length);
+    }
+
+    /** Copy 9 values from the array into the matrix.
+        Depending on the implementation of Matrix, these may be
+        transformed into 16.16 integers in the Matrix, such that
+        a subsequent call to getValues() will not yield exactly
+        the same values.
+    */
+    @Override
+    public void setValues(float[] values) {
+        if (values.length < 9) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        System.arraycopy(values, 0, mValues, 0, mValues.length);
+    }
+    
+    @SuppressWarnings("unused")
+    private final static int kIdentity_Mask      = 0;
+    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
+    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
+    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
+    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
+    private final static int kRectStaysRect_Mask = 0x10;
+    @SuppressWarnings("unused")
+    private final static int kUnknown_Mask       = 0x80;
+    
+    @SuppressWarnings("unused")
+    private final static int kAllMasks           = kTranslate_Mask |
+                                                     kScale_Mask |
+                                                     kAffine_Mask |
+                                                     kPerspective_Mask |
+                                                     kRectStaysRect_Mask;
+
+    // these guys align with the masks, so we can compute a mask from a variable 0/1
+    @SuppressWarnings("unused")
+    private final static int kTranslate_Shift = 0;
+    @SuppressWarnings("unused")
+    private final static int kScale_Shift = 1;
+    @SuppressWarnings("unused")
+    private final static int kAffine_Shift = 2;
+    @SuppressWarnings("unused")
+    private final static int kPerspective_Shift = 3;
+    private final static int kRectStaysRect_Shift = 4;
+
+    private int computeTypeMask() {
+        int mask = 0;
+
+        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
+            mask |= kPerspective_Mask;
+        }
+        
+        if (mValues[2] != 0. || mValues[5] != 0.) {
+            mask |= kTranslate_Mask;
+        }
+    
+        float m00 = mValues[0];
+        float m01 = mValues[1];
+        float m10 = mValues[3];
+        float m11 = mValues[4];
+        
+        if (m01 != 0. || m10 != 0.) {
+            mask |= kAffine_Mask;
+        }
+    
+        if (m00 != 1. || m11 != 1.) {
+            mask |= kScale_Mask;
+        }
+        
+        if ((mask & kPerspective_Mask) == 0) {
+            // map non-zero to 1
+            int im00 = m00 != 0 ? 1 : 0;
+            int im01 = m01 != 0 ? 1 : 0;
+            int im10 = m10 != 0 ? 1 : 0;
+            int im11 = m11 != 0 ? 1 : 0;
+            
+            // record if the (p)rimary and (s)econdary diagonals are all 0 or
+            // all non-zero (answer is 0 or 1)
+            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
+            int dp1 = im00 & im11;        // true if both are 1
+            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
+            int ds1 = im01 & im10;        // true if both are 1
+            
+            // return 1 if primary is 1 and secondary is 0 or
+            // primary is 0 and secondary is 1
+            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+        }
+    
+        return mask;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
new file mode 100644
index 0000000..ade07d6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.SpannedString;
+import android.text.TextUtils;
+
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * A paint implementation overridden by the LayoutLib bridge.
+ */
+public class Paint extends _Original_Paint {
+
+    private int mColor = 0xFFFFFFFF;
+    private float mTextSize = 20;
+    private float mScaleX = 1;
+    private float mSkewX = 0;
+    private Align mAlign = Align.LEFT;
+    private Style mStyle = Style.FILL;
+    private int mFlags = 0;
+    
+    private Font mFont;
+    private final FontRenderContext mFontContext = new FontRenderContext(
+            new AffineTransform(), true, true);
+    private java.awt.FontMetrics mMetrics;
+
+    @SuppressWarnings("hiding")
+    public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
+
+    public static class FontMetrics extends _Original_Paint.FontMetrics {
+    } 
+
+    public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
+    }
+    
+    /**
+     * The Style specifies if the primitive being drawn is filled,
+     * stroked, or both (in the same color). The default is FILL.
+     */
+    public enum Style {
+        /**
+         * Geometry and text drawn with this style will be filled, ignoring all
+         * stroke-related settings in the paint.
+         */
+        FILL            (0),
+        /**
+         * Geometry and text drawn with this style will be stroked, respecting
+         * the stroke-related fields on the paint.
+         */
+        STROKE          (1),
+        /**
+         * Geometry and text drawn with this style will be both filled and
+         * stroked at the same time, respecting the stroke-related fields on
+         * the paint.
+         */
+        FILL_AND_STROKE (2);
+        
+        Style(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    /**
+     * The Cap specifies the treatment for the beginning and ending of
+     * stroked lines and paths. The default is BUTT.
+     */
+    public enum Cap {
+        /**
+         * The stroke ends with the path, and does not project beyond it.
+         */
+        BUTT    (0),
+        /**
+         * The stroke projects out as a square, with the center at the end
+         * of the path.
+         */
+        ROUND   (1),
+        /**
+         * The stroke projects out as a semicircle, with the center at the
+         * end of the path.
+         */
+        SQUARE  (2);
+        
+        private Cap(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    /**
+     * The Join specifies the treatment where lines and curve segments
+     * join on a stroked path. The default is MITER.
+     */
+    public enum Join {
+        /**
+         * The outer edges of a join meet at a sharp angle
+         */
+        MITER   (0),
+        /**
+         * The outer edges of a join meet in a circular arc.
+         */
+        ROUND   (1),
+        /**
+         * The outer edges of a join meet with a straight line
+         */
+        BEVEL   (2);
+        
+        private Join(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    /**
+     * Align specifies how drawText aligns its text relative to the
+     * [x,y] coordinates. The default is LEFT.
+     */
+    public enum Align {
+        /**
+         * The text is drawn to the right of the x,y origin
+         */
+        LEFT    (0),
+        /**
+         * The text is drawn centered horizontally on the x,y origin
+         */
+        CENTER  (1),
+        /**
+         * The text is drawn to the left of the x,y origin
+         */
+        RIGHT   (2);
+        
+        private Align(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    public Paint() {
+        this(0);
+    }
+
+    public Paint(int flags) {
+        setFlags(flags | DEFAULT_PAINT_FLAGS);
+        initFont();
+    }
+
+    public Paint(Paint paint) {
+        set(paint);
+        initFont();
+    }
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    /**
+     * Returns the {@link Font} object.
+     */
+    public Font getFont() {
+        return mFont;
+    }
+    
+    private void initFont() {
+        mTypeface = Typeface.DEFAULT;
+        updateFontObject();
+    }
+    
+    /**
+     * Update the {@link Font} object from the typeface, text size and scaling
+     */
+    private void updateFontObject() {
+        if (mTypeface != null) {
+            // get the typeface font object, and get our font object from it, based on the current size
+            mFont = mTypeface.getFont().deriveFont(mTextSize);
+            if (mScaleX != 1.0 || mSkewX != 0) {
+                // TODO: support skew
+                mFont = mFont.deriveFont(new AffineTransform(
+                        mScaleX, mSkewX, 0, 0, 1, 0));
+            }
+            
+            mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont);
+        }
+    }
+    
+    //----------------------------------------
+    
+    public void set(Paint src) {
+        if (this != src) {
+            mColor = src.mColor;
+            mTextSize = src.mTextSize;
+            mScaleX = src.mScaleX;
+            mSkewX = src.mSkewX;
+            mAlign = src.mAlign;
+            mStyle = src.mStyle;
+            mFlags = src.mFlags;
+
+            super.set(src);
+        }
+    }
+
+    @Override
+    public int getFlags() {
+        return mFlags;
+    }
+
+    @Override
+    public void setFlags(int flags) {
+        mFlags = flags;
+    }
+    
+    /**
+     * Return the font's recommended interline spacing, given the Paint's
+     * settings for typeface, textSize, etc. If metrics is not null, return the
+     * fontmetric values in it.
+     *
+     * @param metrics If this object is not null, its fields are filled with
+     *                the appropriate values given the paint's text attributes.
+     * @return the font's recommended interline spacing.
+     */
+    public float getFontMetrics(FontMetrics metrics) {
+        if (mMetrics != null) {
+            if (metrics != null) {
+                // ascent stuff should be negatif, but awt returns them as positive.
+                metrics.top = - mMetrics.getMaxAscent();
+                metrics.ascent = - mMetrics.getAscent();
+                metrics.descent = mMetrics.getDescent();
+                metrics.bottom = mMetrics.getMaxDescent();
+                metrics.leading = mMetrics.getLeading();
+            }
+    
+            return mMetrics.getHeight();
+        }
+        
+        return 0;
+    }
+
+    public int getFontMetricsInt(FontMetricsInt metrics) {
+        if (mMetrics != null) {
+            if (metrics != null) {
+                // ascent stuff should be negatif, but awt returns them as positive.
+                metrics.top = - mMetrics.getMaxAscent();
+                metrics.ascent = - mMetrics.getAscent();
+                metrics.descent = mMetrics.getDescent();
+                metrics.bottom = mMetrics.getMaxDescent();
+                metrics.leading = mMetrics.getLeading();
+            }
+    
+            return mMetrics.getHeight();
+        }
+        
+        return 0;
+    }
+    
+    /**
+     * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
+     */
+    public FontMetrics getFontMetrics() {
+        FontMetrics fm = new FontMetrics();
+        getFontMetrics(fm);
+        return fm;
+    }
+    
+    /**
+     * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
+     */
+    public FontMetricsInt getFontMetricsInt() {
+        FontMetricsInt fm = new FontMetricsInt();
+        getFontMetricsInt(fm);
+        return fm;
+    }
+
+
+
+    @Override
+    public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
+        // TODO implement if needed
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    @Override
+    public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
+        // TODO implement if needed
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+    
+    @Override
+    public Typeface setTypeface(Typeface typeface) {
+        if (typeface != null) {
+            mTypeface = typeface;
+        } else {
+            mTypeface = Typeface.DEFAULT;
+        }
+        
+        updateFontObject();
+
+        return typeface;
+    }
+    
+    @Override
+    public int getColor() {
+        return mColor;
+    }
+
+    @Override
+    public void setColor(int color) {
+        mColor = color;
+    }
+
+
+    @Override
+    public void setAlpha(int alpha) {
+        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
+    }
+    
+    @Override
+    public int getAlpha() {
+        return mColor >>> 24;
+    }
+    
+    /**
+     * Set or clear the shader object.
+     * <p />
+     * Pass null to clear any previous shader.
+     * As a convenience, the parameter passed is also returned.
+     *
+     * @param shader May be null. the new shader to be installed in the paint
+     * @return       shader
+     */
+    @Override
+    public Shader setShader(Shader shader) {
+        return mShader = shader;
+    }
+
+    /**
+     * Set or clear the paint's colorfilter, returning the parameter.
+     *
+     * @param filter May be null. The new filter to be installed in the paint
+     * @return       filter
+     */
+    @Override
+    public ColorFilter setColorFilter(ColorFilter filter) {
+        int filterNative = 0;
+        if (filter != null)
+            filterNative = filter.native_instance;
+        mColorFilter = filter;
+        return filter;
+    }
+
+    /**
+     * Set or clear the xfermode object.
+     * <p />
+     * Pass null to clear any previous xfermode.
+     * As a convenience, the parameter passed is also returned.
+     *
+     * @param xfermode May be null. The xfermode to be installed in the paint
+     * @return         xfermode
+     */
+    @Override
+    public Xfermode setXfermode(Xfermode xfermode) {
+        return mXfermode = xfermode;
+    }
+    
+    public void setTextAlign(Align align) {
+        mAlign = align;
+    }
+    
+    @Override
+    public void setTextAlign(android.graphics._Original_Paint.Align align) {
+        // TODO implement if needed
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+    
+    public Align getTextAlign() {
+        return mAlign;
+    }
+    
+    public void setStyle(Style style) {
+        mStyle = style;
+    }
+
+    @Override
+    public void setStyle(android.graphics._Original_Paint.Style style) {
+        // TODO implement if needed
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    public Style getStyle() {
+        return mStyle;
+    }
+    
+    @Override
+    public void setDither(boolean dither) {
+        mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
+    }
+    
+    @Override
+    public void setAntiAlias(boolean aa) {
+        mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
+    }
+    
+    @Override
+    public void setFakeBoldText(boolean flag) {
+        mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
+    }
+
+    /**
+     * Return the paint's text size.
+     *
+     * @return the paint's text size.
+     */
+    @Override
+    public float getTextSize() {
+        return mTextSize;
+    }
+
+    /**
+     * Set the paint's text size. This value must be > 0
+     *
+     * @param textSize set the paint's text size.
+     */
+    @Override
+    public void setTextSize(float textSize) {
+        mTextSize = textSize;
+        
+        updateFontObject();
+    }
+
+    /**
+     * Return the paint's horizontal scale factor for text. The default value
+     * is 1.0.
+     *
+     * @return the paint's scale factor in X for drawing/measuring text
+     */
+    @Override
+    public float getTextScaleX() {
+        return mScaleX;
+    }
+
+    /**
+     * Set the paint's horizontal scale factor for text. The default value
+     * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
+     * stretch the text narrower.
+     *
+     * @param scaleX set the paint's scale in X for drawing/measuring text.
+     */
+    @Override
+    public void setTextScaleX(float scaleX) {
+        mScaleX = scaleX;
+        
+        updateFontObject();
+    }
+
+    /**
+     * Return the paint's horizontal skew factor for text. The default value
+     * is 0.
+     *
+     * @return         the paint's skew factor in X for drawing text.
+     */
+    @Override
+    public float getTextSkewX() {
+        return mSkewX;
+    }
+
+    /**
+     * Set the paint's horizontal skew factor for text. The default value
+     * is 0. For approximating oblique text, use values around -0.25.
+     *
+     * @param skewX set the paint's skew factor in X for drawing text.
+     */
+    @Override
+    public void setTextSkewX(float skewX) {
+        mSkewX = skewX;
+        
+        updateFontObject();
+    }
+
+    /**
+     * Return the distance above (negative) the baseline (ascent) based on the
+     * current typeface and text size.
+     *
+     * @return the distance above (negative) the baseline (ascent) based on the
+     *         current typeface and text size.
+     */
+    @Override
+    public float ascent() {
+        if (mMetrics != null) {
+            // ascent stuff should be negatif, but awt returns them as positive.
+            return - mMetrics.getAscent();
+        }
+        
+        return 0;
+    }
+
+    /**
+     * Return the distance below (positive) the baseline (descent) based on the
+     * current typeface and text size.
+     *
+     * @return the distance below (positive) the baseline (descent) based on
+     *         the current typeface and text size.
+     */
+    @Override
+    public float descent() {
+        if (mMetrics != null) {
+            return mMetrics.getDescent();
+        }
+        
+        return 0;
+    }
+    
+    /**
+     * Return the width of the text.
+     *
+     * @param text  The text to measure
+     * @param index The index of the first character to start measuring
+     * @param count THe number of characters to measure, beginning with start
+     * @return      The width of the text
+     */
+    @Override
+    public float measureText(char[] text, int index, int count) {
+        if (mFont != null && text != null && text.length > 0) {
+            Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext);
+            
+            return (float)bounds.getWidth();
+        }
+        
+        return 0;
+    }
+
+    /**
+     * Return the width of the text.
+     *
+     * @param text  The text to measure
+     * @param start The index of the first character to start measuring
+     * @param end   1 beyond the index of the last character to measure
+     * @return      The width of the text
+     */
+    @Override
+    public float measureText(String text, int start, int end) {
+        return measureText(text.toCharArray(), start, end - start);
+    }
+
+    /**
+     * Return the width of the text.
+     *
+     * @param text  The text to measure
+     * @return      The width of the text
+     */
+    @Override
+    public float measureText(String text) {
+        return measureText(text.toCharArray(), 0, text.length());
+    }
+    
+    /*
+     * re-implement to call SpannableStringBuilder.measureText with a Paint object
+     * instead of an _Original_Paint
+     */
+    @Override
+    public float measureText(CharSequence text, int start, int end) {
+        if (text instanceof String) {
+            return measureText((String)text, start, end);
+        }
+        if (text instanceof SpannedString ||
+            text instanceof SpannableString) {
+            return measureText(text.toString(), start, end);
+        }
+        if (text instanceof SpannableStringBuilder) {
+            return ((SpannableStringBuilder)text).measureText(start, end, this);
+        }
+
+        char[] buf = TemporaryBuffer.obtain(end - start);
+        TextUtils.getChars(text, start, end, buf, 0);
+        float result = measureText(buf, 0, end - start);
+        TemporaryBuffer.recycle(buf);
+        return result;
+    }
+    
+    /**
+     * Measure the text, stopping early if the measured width exceeds maxWidth.
+     * Return the number of chars that were measured, and if measuredWidth is
+     * not null, return in it the actual width measured.
+     *
+     * @param text  The text to measure
+     * @param index The offset into text to begin measuring at
+     * @param count The number of maximum number of entries to measure. If count
+     *              is negative, then the characters before index are measured
+     *              in reverse order. This allows for measuring the end of
+     *              string.
+     * @param maxWidth The maximum width to accumulate.
+     * @param measuredWidth Optional. If not null, returns the actual width
+     *                     measured.
+     * @return The number of chars that were measured. Will always be <=
+     *         abs(count).
+     */
+    @Override
+    public int breakText(char[] text, int index, int count,
+                                float maxWidth, float[] measuredWidth) {
+        int inc = count > 0 ? 1 : -1;
+        
+        int measureIndex = 0;
+        float measureAcc = 0;
+        for (int i = index ; i != index + count ; i += inc, measureIndex++) {
+            int start, end;
+            if (i < index) {
+                start = i;
+                end = index;
+            } else {
+                start = index;
+                end = i;
+            }
+            
+            // measure from start to end
+            float res = measureText(text, start, end - start + 1);
+            
+            if (measuredWidth != null) {
+                measuredWidth[measureIndex] = res;
+            }
+            
+            measureAcc += res;
+            if (res > maxWidth) {
+                // we should not return this char index, but since it's 0-based and we need
+                // to return a count, we simply return measureIndex;
+                return measureIndex;
+            }
+            
+        }
+        
+        return measureIndex;
+    }
+
+    /**
+     * Measure the text, stopping early if the measured width exceeds maxWidth.
+     * Return the number of chars that were measured, and if measuredWidth is
+     * not null, return in it the actual width measured.
+     *
+     * @param text  The text to measure
+     * @param measureForwards If true, measure forwards, starting at index.
+     *                        Otherwise, measure backwards, starting with the
+     *                        last character in the string.
+     * @param maxWidth The maximum width to accumulate.
+     * @param measuredWidth Optional. If not null, returns the actual width
+     *                     measured.
+     * @return The number of chars that were measured. Will always be <=
+     *         abs(count).
+     */
+    @Override
+    public int breakText(String text, boolean measureForwards,
+                                float maxWidth, float[] measuredWidth) {
+        // NOTE: javadoc doesn't match. Just a guess.
+        return breakText(text,
+                0 /* start */, text.length() /* end */,
+                measureForwards, maxWidth, measuredWidth);
+    }
+
+    /**
+     * Return the advance widths for the characters in the string.
+     *
+     * @param text     The text to measure
+     * @param index    The index of the first char to to measure
+     * @param count    The number of chars starting with index to measure
+     * @param widths   array to receive the advance widths of the characters.
+     *                 Must be at least a large as count.
+     * @return         the actual number of widths returned.
+     */
+    @Override
+    public int getTextWidths(char[] text, int index, int count,
+                             float[] widths) {
+        if (mMetrics != null) {
+            if ((index | count) < 0 || index + count > text.length
+                    || count > widths.length) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
+    
+            for (int i = 0; i < count; i++) {
+                widths[i] = mMetrics.charWidth(text[i + index]);
+            }
+            
+            return count;
+        }
+        
+        return 0;
+    }
+
+    /**
+     * Return the advance widths for the characters in the string.
+     *
+     * @param text   The text to measure
+     * @param start  The index of the first char to to measure
+     * @param end    The end of the text slice to measure
+     * @param widths array to receive the advance widths of the characters.
+     *               Must be at least a large as the text.
+     * @return       the number of unichars in the specified text.
+     */
+    @Override
+    public int getTextWidths(String text, int start, int end, float[] widths) {
+        if ((start | end | (end - start) | (text.length() - end)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (end - start > widths.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        
+        return getTextWidths(text.toCharArray(), start, end - start, widths);
+    }
+    
+    /*
+     * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
+     * instead of an _Original_Paint
+     */
+    @Override
+    public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
+        if (text instanceof String) {
+            return getTextWidths((String)text, start, end, widths);
+        }
+        if (text instanceof SpannedString || text instanceof SpannableString) {
+            return getTextWidths(text.toString(), start, end, widths);
+        }
+        if (text instanceof SpannableStringBuilder) {
+            return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
+        }
+
+        char[] buf = TemporaryBuffer.obtain(end - start);
+        TextUtils.getChars(text, start, end, buf, 0);
+        int result = getTextWidths(buf, 0, end - start, widths);
+        TemporaryBuffer.recycle(buf);
+        return result;
+    }
+
+
+    /**
+     * Return the path (outline) for the specified text.
+     * Note: just like Canvas.drawText, this will respect the Align setting in
+     * the paint.
+     *
+     * @param text     The text to retrieve the path from
+     * @param index    The index of the first character in text
+     * @param count    The number of characterss starting with index
+     * @param x        The x coordinate of the text's origin
+     * @param y        The y coordinate of the text's origin
+     * @param path     The path to receive the data describing the text. Must
+     *                 be allocated by the caller.
+     */
+    @Override
+    public void getTextPath(char[] text, int index, int count,
+                            float x, float y, Path path) {
+
+        // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
+        
+        if ((index | count) < 0 || index + count > text.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        
+        // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
+        
+        throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
+    }
+
+    /**
+     * Return the path (outline) for the specified text.
+     * Note: just like Canvas.drawText, this will respect the Align setting
+     * in the paint.
+     *
+     * @param text  The text to retrieve the path from
+     * @param start The first character in the text
+     * @param end   1 past the last charcter in the text
+     * @param x     The x coordinate of the text's origin
+     * @param y     The y coordinate of the text's origin
+     * @param path  The path to receive the data describing the text. Must
+     *              be allocated by the caller.
+     */
+    @Override
+    public void getTextPath(String text, int start, int end,
+                            float x, float y, Path path) {
+        if ((start | end | (end - start) | (text.length() - end)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        
+        getTextPath(text.toCharArray(), start, end - start, x, y, path);
+    }
+    
+    /**
+     * Return in bounds (allocated by the caller) the smallest rectangle that
+     * encloses all of the characters, with an implied origin at (0,0).
+     *
+     * @param text  String to measure and return its bounds
+     * @param start Index of the first char in the string to measure
+     * @param end   1 past the last char in the string measure
+     * @param bounds Returns the unioned bounds of all the text. Must be
+     *               allocated by the caller.
+     */
+    @Override
+    public void getTextBounds(String text, int start, int end, Rect bounds) {
+        if ((start | end | (end - start) | (text.length() - end)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (bounds == null) {
+            throw new NullPointerException("need bounds Rect");
+        }
+        
+        getTextBounds(text.toCharArray(), start, end - start, bounds);
+    }
+    
+    /**
+     * Return in bounds (allocated by the caller) the smallest rectangle that
+     * encloses all of the characters, with an implied origin at (0,0).
+     *
+     * @param text  Array of chars to measure and return their unioned bounds
+     * @param index Index of the first char in the array to measure
+     * @param count The number of chars, beginning at index, to measure
+     * @param bounds Returns the unioned bounds of all the text. Must be
+     *               allocated by the caller.
+     */
+    @Override
+    public void getTextBounds(char[] text, int index, int count, Rect bounds) {
+        if (mFont != null) {
+            if ((index | count) < 0 || index + count > text.length) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
+            if (bounds == null) {
+                throw new NullPointerException("need bounds Rect");
+            }
+            
+            Rectangle2D rect = mFont.getStringBounds(text, index, index + count, mFontContext);
+            bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
new file mode 100644
index 0000000..12d2cde
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Path.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * The Path class encapsulates compound (multiple contour) geometric paths
+ * consisting of straight line segments, quadratic curves, and cubic curves.
+ * It can be drawn with canvas.drawPath(path, paint), either filled or stroked
+ * (based on the paint's Style), or it can be used for clipping or to draw
+ * text on a path.
+ */
+public class Path {
+    
+    private FillType mFillType = FillType.WINDING;
+    private GeneralPath mPath = new GeneralPath();
+    
+    private float mLastX = 0;
+    private float mLastY = 0;
+    
+    //---------- Custom methods ----------
+
+    public Shape getAwtShape() {
+        return mPath;
+    }
+
+    //----------
+
+    /**
+     * Create an empty path
+     */
+    public Path() {
+    }
+
+    /**
+     * Create a new path, copying the contents from the src path.
+     *
+     * @param src The path to copy from when initializing the new path
+     */
+    public Path(Path src) {
+        mPath.append(src.mPath, false /* connect */);
+    }
+    
+    /**
+     * Clear any lines and curves from the path, making it empty.
+     * This does NOT change the fill-type setting.
+     */
+    public void reset() {
+        mPath = new GeneralPath();
+    }
+
+    /**
+     * Rewinds the path: clears any lines and curves from the path but
+     * keeps the internal data structure for faster reuse.
+     */
+    public void rewind() {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /** Replace the contents of this with the contents of src.
+    */
+    public void set(Path src) {
+        mPath.append(src.mPath, false /* connect */);
+    }
+
+    /** Enum for the ways a path may be filled
+    */
+    public enum FillType {
+        // these must match the values in SkPath.h
+        WINDING         (GeneralPath.WIND_NON_ZERO, false),
+        EVEN_ODD        (GeneralPath.WIND_EVEN_ODD, false),
+        INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
+        INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
+        
+        FillType(int rule, boolean inverse) {
+            this.rule = rule;
+            this.inverse = inverse;
+        }
+
+        final int rule;
+        final boolean inverse;
+    }
+    
+    /**
+     * Return the path's fill type. This defines how "inside" is
+     * computed. The default value is WINDING.
+     *
+     * @return the path's fill type
+     */
+    public FillType getFillType() {
+        return mFillType;
+    }
+
+    /**
+     * Set the path's fill type. This defines how "inside" is computed.
+     *
+     * @param ft The new fill type for this path
+     */
+    public void setFillType(FillType ft) {
+        mFillType = ft;
+        mPath.setWindingRule(ft.rule);
+    }
+    
+    /**
+     * Returns true if the filltype is one of the INVERSE variants
+     *
+     * @return true if the filltype is one of the INVERSE variants
+     */
+    public boolean isInverseFillType() {
+        return mFillType.inverse;
+    }
+    
+    /**
+     * Toggles the INVERSE state of the filltype
+     */
+    public void toggleInverseFillType() {
+        switch (mFillType) {
+            case WINDING:
+                mFillType = FillType.INVERSE_WINDING;
+                break;
+            case EVEN_ODD:
+                mFillType = FillType.INVERSE_EVEN_ODD;
+                break;
+            case INVERSE_WINDING:
+                mFillType = FillType.WINDING;
+                break;
+            case INVERSE_EVEN_ODD:
+                mFillType = FillType.EVEN_ODD;
+                break;
+        }
+    }
+    
+    /**
+     * Returns true if the path is empty (contains no lines or curves)
+     *
+     * @return true if the path is empty (contains no lines or curves)
+     */
+    public boolean isEmpty() {
+        return mPath.getCurrentPoint() == null;
+    }
+
+    /**
+     * Returns true if the path specifies a rectangle. If so, and if rect is
+     * not null, set rect to the bounds of the path. If the path does not
+     * specify a rectangle, return false and ignore rect.
+     *
+     * @param rect If not null, returns the bounds of the path if it specifies
+     *             a rectangle
+     * @return     true if the path specifies a rectangle
+     */
+    public boolean isRect(RectF rect) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Compute the bounds of the path, and write the answer into bounds. If the
+     * path contains 0 or 1 points, the bounds is set to (0,0,0,0)
+     *
+     * @param bounds Returns the computed bounds of the path
+     * @param exact If true, return the exact (but slower) bounds, else return
+     *              just the bounds of all control points
+     */
+    public void computeBounds(RectF bounds, boolean exact) {
+        Rectangle2D rect = mPath.getBounds2D();
+        bounds.left = (float)rect.getMinX();
+        bounds.right = (float)rect.getMaxX();
+        bounds.top = (float)rect.getMinY();
+        bounds.bottom = (float)rect.getMaxY();
+    }
+
+    /**
+     * Hint to the path to prepare for adding more points. This can allow the
+     * path to more efficiently allocate its storage.
+     *
+     * @param extraPtCount The number of extra points that may be added to this
+     *                     path
+     */
+    public void incReserve(int extraPtCount) {
+        // pass
+    }
+
+    /**
+     * Set the beginning of the next contour to the point (x,y).
+     *
+     * @param x The x-coordinate of the start of a new contour
+     * @param y The y-coordinate of the start of a new contour
+     */
+    public void moveTo(float x, float y) {
+        mPath.moveTo(mLastX = x, mLastY = y);
+    }
+
+    /**
+     * Set the beginning of the next contour relative to the last point on the
+     * previous contour. If there is no previous contour, this is treated the
+     * same as moveTo().
+     *
+     * @param dx The amount to add to the x-coordinate of the end of the
+     *           previous contour, to specify the start of a new contour
+     * @param dy The amount to add to the y-coordinate of the end of the
+     *           previous contour, to specify the start of a new contour
+     */
+    public void rMoveTo(float dx, float dy) {
+        dx += mLastX;
+        dy += mLastY;
+        mPath.moveTo(mLastX = dx, mLastY = dy);
+    }
+
+    /**
+     * Add a line from the last point to the specified point (x,y).
+     * If no moveTo() call has been made for this contour, the first point is
+     * automatically set to (0,0).
+     *
+     * @param x The x-coordinate of the end of a line
+     * @param y The y-coordinate of the end of a line
+     */
+    public void lineTo(float x, float y) {
+        mPath.lineTo(mLastX = x, mLastY = y);
+    }
+
+    /**
+     * Same as lineTo, but the coordinates are considered relative to the last
+     * point on this contour. If there is no previous point, then a moveTo(0,0)
+     * is inserted automatically.
+     *
+     * @param dx The amount to add to the x-coordinate of the previous point on
+     *           this contour, to specify a line
+     * @param dy The amount to add to the y-coordinate of the previous point on
+     *           this contour, to specify a line
+     */
+    public void rLineTo(float dx, float dy) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx += mLastX;
+        dy += mLastY;
+        mPath.lineTo(mLastX = dx, mLastY = dy);
+    }
+
+    /**
+     * Add a quadratic bezier from the last point, approaching control point
+     * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
+     * this contour, the first point is automatically set to (0,0).
+     *
+     * @param x1 The x-coordinate of the control point on a quadratic curve
+     * @param y1 The y-coordinate of the control point on a quadratic curve
+     * @param x2 The x-coordinate of the end point on a quadratic curve
+     * @param y2 The y-coordinate of the end point on a quadratic curve
+     */
+    public void quadTo(float x1, float y1, float x2, float y2) {
+        mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
+    }
+
+    /**
+     * Same as quadTo, but the coordinates are considered relative to the last
+     * point on this contour. If there is no previous point, then a moveTo(0,0)
+     * is inserted automatically.
+     *
+     * @param dx1 The amount to add to the x-coordinate of the last point on
+     *            this contour, for the control point of a quadratic curve
+     * @param dy1 The amount to add to the y-coordinate of the last point on
+     *            this contour, for the control point of a quadratic curve
+     * @param dx2 The amount to add to the x-coordinate of the last point on
+     *            this contour, for the end point of a quadratic curve
+     * @param dy2 The amount to add to the y-coordinate of the last point on
+     *            this contour, for the end point of a quadratic curve
+     */
+    public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx1 += mLastX;
+        dy1 += mLastY;
+        dx2 += mLastX;
+        dy2 += mLastY;
+        mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
+    }
+
+    /**
+     * Add a cubic bezier from the last point, approaching control points
+     * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
+     * made for this contour, the first point is automatically set to (0,0).
+     *
+     * @param x1 The x-coordinate of the 1st control point on a cubic curve
+     * @param y1 The y-coordinate of the 1st control point on a cubic curve
+     * @param x2 The x-coordinate of the 2nd control point on a cubic curve
+     * @param y2 The y-coordinate of the 2nd control point on a cubic curve
+     * @param x3 The x-coordinate of the end point on a cubic curve
+     * @param y3 The y-coordinate of the end point on a cubic curve
+     */
+    public void cubicTo(float x1, float y1, float x2, float y2,
+                        float x3, float y3) {
+        mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
+    }
+
+    /**
+     * Same as cubicTo, but the coordinates are considered relative to the
+     * current point on this contour. If there is no previous point, then a
+     * moveTo(0,0) is inserted automatically.
+     */
+    public void rCubicTo(float dx1, float dy1, float dx2, float dy2,
+                         float dx3, float dy3) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx1 += mLastX;
+        dy1 += mLastY;
+        dx2 += mLastX;
+        dy2 += mLastY;
+        dx3 += mLastX;
+        dy3 += mLastY;
+        mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
+    }
+
+    /**
+     * Append the specified arc to the path as a new contour. If the start of
+     * the path is different from the path's current last point, then an
+     * automatic lineTo() is added to connect the current contour to the
+     * start of the arc. However, if the path is empty, then we call moveTo()
+     * with the first point of the arc. The sweep angle is tread mod 360.
+     *
+     * @param oval        The bounds of oval defining shape and size of the arc
+     * @param startAngle  Starting angle (in degrees) where the arc begins
+     * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
+     *                    mod 360.
+     * @param forceMoveTo If true, always begin a new contour with the arc
+     */
+    public void arcTo(RectF oval, float startAngle, float sweepAngle,
+                      boolean forceMoveTo) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Append the specified arc to the path as a new contour. If the start of
+     * the path is different from the path's current last point, then an
+     * automatic lineTo() is added to connect the current contour to the
+     * start of the arc. However, if the path is empty, then we call moveTo()
+     * with the first point of the arc.
+     *
+     * @param oval        The bounds of oval defining shape and size of the arc
+     * @param startAngle  Starting angle (in degrees) where the arc begins
+     * @param sweepAngle  Sweep angle (in degrees) measured clockwise
+     */
+    public void arcTo(RectF oval, float startAngle, float sweepAngle) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Close the current contour. If the current point is not equal to the
+     * first point of the contour, a line segment is automatically added.
+     */
+    public void close() {
+        mPath.closePath();
+    }
+
+    /**
+     * Specifies how closed shapes (e.g. rects, ovals) are oriented when they
+     * are added to a path.
+     */
+    public enum Direction {
+        /** clockwise */
+        CW  (0),    // must match enum in SkPath.h
+        /** counter-clockwise */
+        CCW (1);    // must match enum in SkPath.h
+        
+        Direction(int ni) {
+            nativeInt = ni;
+        }
+        final int nativeInt;
+    }
+    
+    /**
+     * Add a closed rectangle contour to the path
+     *
+     * @param rect The rectangle to add as a closed contour to the path
+     * @param dir  The direction to wind the rectangle's contour
+     */
+    public void addRect(RectF rect, Direction dir) {
+        if (rect == null) {
+            throw new NullPointerException("need rect parameter");
+        }
+        
+        addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
+    }
+
+    /**
+     * Add a closed rectangle contour to the path
+     *
+     * @param left   The left side of a rectangle to add to the path
+     * @param top    The top of a rectangle to add to the path
+     * @param right  The right side of a rectangle to add to the path
+     * @param bottom The bottom of a rectangle to add to the path
+     * @param dir    The direction to wind the rectangle's contour
+     */
+    public void addRect(float left, float top, float right, float bottom,
+                        Direction dir) {
+        moveTo(left, top);
+
+        switch (dir) {
+            case CW:
+                lineTo(right, top);
+                lineTo(right, bottom);
+                lineTo(left, bottom);
+                break;
+            case CCW:
+                lineTo(left, bottom);
+                lineTo(right, bottom);
+                lineTo(right, top);
+                break;
+        }
+
+        close();
+    }
+
+    /**
+     * Add a closed oval contour to the path
+     *
+     * @param oval The bounds of the oval to add as a closed contour to the path
+     * @param dir  The direction to wind the oval's contour
+     */
+    public void addOval(RectF oval, Direction dir) {
+        if (oval == null) {
+            throw new NullPointerException("need oval parameter");
+        }
+
+        // FIXME Need to support direction
+        Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
+        
+        mPath.append(ovalShape, false /* connect */);
+    }
+
+    /**
+     * Add a closed circle contour to the path
+     *
+     * @param x   The x-coordinate of the center of a circle to add to the path
+     * @param y   The y-coordinate of the center of a circle to add to the path
+     * @param radius The radius of a circle to add to the path
+     * @param dir    The direction to wind the circle's contour
+     */
+    public void addCircle(float x, float y, float radius, Direction dir) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Add the specified arc to the path as a new contour.
+     *
+     * @param oval The bounds of oval defining the shape and size of the arc
+     * @param startAngle Starting angle (in degrees) where the arc begins
+     * @param sweepAngle Sweep angle (in degrees) measured clockwise
+     */
+    public void addArc(RectF oval, float startAngle, float sweepAngle) {
+        if (oval == null) {
+            throw new NullPointerException("need oval parameter");
+        }
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+        * Add a closed round-rectangle contour to the path
+     *
+     * @param rect The bounds of a round-rectangle to add to the path
+     * @param rx   The x-radius of the rounded corners on the round-rectangle
+     * @param ry   The y-radius of the rounded corners on the round-rectangle
+     * @param dir  The direction to wind the round-rectangle's contour
+     */
+    public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
+        if (rect == null) {
+            throw new NullPointerException("need rect parameter");
+        }
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Add a closed round-rectangle contour to the path. Each corner receives
+     * two radius values [X, Y]. The corners are ordered top-left, top-right,
+     * bottom-right, bottom-left
+     *
+     * @param rect The bounds of a round-rectangle to add to the path
+     * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+     * @param dir  The direction to wind the round-rectangle's contour
+     */
+    public void addRoundRect(RectF rect, float[] radii, Direction dir) {
+        if (rect == null) {
+            throw new NullPointerException("need rect parameter");
+        }
+        if (radii.length < 8) {
+            throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
+        }
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Add a copy of src to the path, offset by (dx,dy)
+     *
+     * @param src The path to add as a new contour
+     * @param dx  The amount to translate the path in X as it is added
+     */
+    public void addPath(Path src, float dx, float dy) {
+        PathIterator iterator = src.mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
+        mPath.append(iterator, false /* connect */);
+    }
+
+    /**
+     * Add a copy of src to the path
+     *
+     * @param src The path that is appended to the current path
+     */
+    public void addPath(Path src) {
+        addPath(src, 0, 0);
+    }
+
+    /**
+     * Add a copy of src to the path, transformed by matrix
+     *
+     * @param src The path to add as a new contour
+     */
+    public void addPath(Path src, Matrix matrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Offset the path by (dx,dy), returning true on success
+     *
+     * @param dx  The amount in the X direction to offset the entire path
+     * @param dy  The amount in the Y direction to offset the entire path
+     * @param dst The translated path is written here. If this is null, then
+     *            the original path is modified.
+     */
+    public void offset(float dx, float dy, Path dst) {
+        GeneralPath newPath = new GeneralPath();
+        
+        PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
+        
+        newPath.append(iterator, false /* connect */);
+        
+        if (dst != null) {
+            dst.mPath = newPath;
+        } else {
+            mPath = newPath;
+        }
+    }
+
+    /**
+     * Offset the path by (dx,dy), returning true on success
+     *
+     * @param dx The amount in the X direction to offset the entire path
+     * @param dy The amount in the Y direction to offset the entire path
+     */
+    public void offset(float dx, float dy) {
+        offset(dx, dy, null /* dst */);
+    }
+
+    /**
+     * Sets the last point of the path.
+     *
+     * @param dx The new X coordinate for the last point
+     * @param dy The new Y coordinate for the last point
+     */
+    public void setLastPoint(float dx, float dy) {
+        mLastX = dx;
+        mLastY = dy;
+    }
+
+    /**
+     * Transform the points in this path by matrix, and write the answer
+     * into dst. If dst is null, then the the original path is modified.
+     *
+     * @param matrix The matrix to apply to the path
+     * @param dst    The transformed path is written here. If dst is null,
+     *               then the the original path is modified
+     */
+    public void transform(Matrix matrix, Path dst) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Transform the points in this path by matrix.
+     *
+     * @param matrix The matrix to apply to the path
+     */
+    public void transform(Matrix matrix) {
+        transform(matrix, null /* dst */);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
new file mode 100644
index 0000000..974ae49
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.graphics.PorterDuff.Mode;
+
+public class PorterDuffXfermode extends Xfermode {
+    private final Mode mMode;
+
+    /**
+     * Create an xfermode that uses the specified porter-duff mode.
+     *
+     * @param mode           The porter-duff mode that is applied
+     */
+    public PorterDuffXfermode(PorterDuff.Mode mode) {
+        mMode = mode;
+    }
+    
+    //---------- Custom Methods
+    
+    public PorterDuff.Mode getMode() {
+        return mMode;
+    }
+    
+    //----------
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
new file mode 100644
index 0000000..61b693a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+public class RadialGradient extends Shader {
+
+   /** Create a shader that draws a radial gradient given the center and radius.
+       @param x        The x-coordinate of the center of the radius
+       @param y        The y-coordinate of the center of the radius
+       @param radius   Must be positive. The radius of the circle for this gradient
+       @param colors   The colors to be distributed between the center and edge of the circle
+       @param positions May be NULL. The relative position of
+                       each corresponding color in the colors array. If this is NULL,
+                       the the colors are distributed evenly between the center and edge of the circle.
+       @param  tile    The Shader tiling mode
+   */
+   public RadialGradient(float x, float y, float radius,
+                         int colors[], float positions[], TileMode tile) {
+       if (radius <= 0) {
+           throw new IllegalArgumentException("radius must be > 0");
+       }
+       if (colors.length < 2) {
+           throw new IllegalArgumentException("needs >= 2 number of colors");
+       }
+       if (positions != null && colors.length != positions.length) {
+           throw new IllegalArgumentException("color and position arrays must be of equal length");
+       }
+
+       // FIXME Implement shader
+   }
+
+   /** Create a shader that draws a radial gradient given the center and radius.
+       @param x        The x-coordinate of the center of the radius
+       @param y        The y-coordinate of the center of the radius
+       @param radius   Must be positive. The radius of the circle for this gradient
+       @param color0   The color at the center of the circle.
+       @param color1   The color at the edge of the circle.
+       @param tile     The Shader tiling mode
+   */
+   public RadialGradient(float x, float y, float radius,
+                         int color0, int color1, TileMode tile) {
+       if (radius <= 0) {
+           throw new IllegalArgumentException("radius must be > 0");
+       }
+       // FIXME Implement shader
+   }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
new file mode 100644
index 0000000..3a9fda5
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+/**
+ * Shader is the based class for objects that return horizontal spans of colors
+ * during drawing. A subclass of Shader is installed in a Paint calling
+ * paint.setShader(shader). After that any object (other than a bitmap) that is
+ * drawn with that paint will get its color(s) from the shader.
+ */
+public class Shader {
+    
+    private final Matrix mMatrix = new Matrix();
+
+    public enum TileMode {
+        /**
+         * replicate the edge color if the shader draws outside of its
+         * original bounds
+         */
+        CLAMP   (0),
+        /**
+         * repeat the shader's image horizontally and vertically
+         */
+        REPEAT  (1),
+        /**
+         * repeat the shader's image horizontally and vertically, alternating
+         * mirror images so that adjacent images always seam
+         */
+        MIRROR  (2);
+    
+        TileMode(int nativeInt) {
+            this.nativeInt = nativeInt;
+        }
+        final int nativeInt;
+    }
+
+    /**
+     * Return true if the shader has a non-identity local matrix.
+     * @param localM If not null, it is set to the shader's local matrix.
+     * @return true if the shader has a non-identity local matrix
+     */
+    public boolean getLocalMatrix(Matrix localM) {
+        if (localM != null) {
+            localM.set(mMatrix);
+        }
+        
+        return !mMatrix.isIdentity();
+    }
+
+    /**
+     * Set the shader's local matrix. Passing null will reset the shader's
+     * matrix to identity
+     * @param localM The shader's new local matrix, or null to specify identity
+     */
+    public void setLocalMatrix(Matrix localM) {
+        if (localM != null) {
+            mMatrix.set(localM);
+        } else {
+            mMatrix.reset();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
new file mode 100644
index 0000000..e79e970
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+public class SweepGradient extends Shader {
+
+    /**
+     * A subclass of Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
+     * @param colors   The colors to be distributed between around the center.
+     *                 There must be at least 2 colors in the array.
+     * @param positions May be NULL. The relative position of
+     *                 each corresponding color in the colors array, beginning
+     *                 with 0 and ending with 1.0. If the values are not
+     *                 monotonic, the drawing may produce unexpected results.
+     *                 If positions is NULL, then the colors are automatically
+     *                 spaced evenly.
+     */
+    public SweepGradient(float cx, float cy,
+                         int colors[], float positions[]) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException(
+                        "color and position arrays must be of equal length");
+        }
+        
+        // FIXME Implement shader
+    }
+
+    /**
+     * A subclass of Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
+     * @param color0   The color to use at the start of the sweep
+     * @param color1   The color to use at the end of the sweep
+     */
+    public SweepGradient(float cx, float cy, int color0, int color1) {
+        // FIXME Implement shader
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
new file mode 100644
index 0000000..e878b04
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.FontLoader;
+
+import android.content.res.AssetManager;
+
+import java.awt.Font;
+
+/**
+ * Re-implementation of Typeface over java.awt 
+ */
+public class Typeface {
+    private static final String DEFAULT_FAMILY = "sans-serif";
+    private static final int[] styleBuffer = new int[1];
+
+    /** The default NORMAL typeface object */
+    public static Typeface DEFAULT;
+    /**
+     * The default BOLD typeface object. Note: this may be not actually be
+     * bold, depending on what fonts are installed. Call getStyle() to know
+     * for sure.
+     */
+    public static Typeface DEFAULT_BOLD;
+    /** The NORMAL style of the default sans serif typeface. */
+    public static Typeface SANS_SERIF;
+    /** The NORMAL style of the default serif typeface. */
+    public static Typeface SERIF;
+    /** The NORMAL style of the default monospace typeface. */
+    public static Typeface MONOSPACE;
+
+    private static Typeface[] sDefaults;
+    private static FontLoader mFontLoader;
+    
+    private final int mStyle;
+    private final Font mFont;
+    private final String mFamily;
+    
+    // Style
+    public static final int NORMAL = _Original_Typeface.NORMAL;
+    public static final int BOLD = _Original_Typeface.BOLD;
+    public static final int ITALIC = _Original_Typeface.ITALIC;
+    public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC;
+
+    /**
+     * Returns the underlying {@link Font} object.
+     */
+    public Font getFont() {
+        return mFont;
+    }
+    
+    /** Returns the typeface's intrinsic style attributes */
+    public int getStyle() {
+        return mStyle;
+    }
+
+    /** Returns true if getStyle() has the BOLD bit set. */
+    public final boolean isBold() {
+        return (getStyle() & BOLD) != 0;
+    }
+
+    /** Returns true if getStyle() has the ITALIC bit set. */
+    public final boolean isItalic() {
+        return (getStyle() & ITALIC) != 0;
+    }
+
+    /**
+     * Create a typeface object given a family name, and option style information.
+     * If null is passed for the name, then the "default" font will be chosen.
+     * The resulting typeface object can be queried (getStyle()) to discover what
+     * its "real" style characteristics are.
+     *
+     * @param familyName May be null. The name of the font family.
+     * @param style  The style (normal, bold, italic) of the typeface.
+     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
+     * @return The best matching typeface.
+     */
+    public static Typeface create(String familyName, int style) {
+        styleBuffer[0] = style;
+        Font font = mFontLoader.getFont(familyName, styleBuffer);
+        if (font != null) {
+            return new Typeface(familyName, styleBuffer[0], font);
+        }
+        
+        return null;
+    }
+
+    /**
+     * Create a typeface object that best matches the specified existing
+     * typeface and the specified Style. Use this call if you want to pick a new
+     * style from the same family of an existing typeface object. If family is
+     * null, this selects from the default font's family.
+     *
+     * @param family May be null. The name of the existing type face.
+     * @param style  The style (normal, bold, italic) of the typeface.
+     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
+     * @return The best matching typeface.
+     */
+    public static Typeface create(Typeface family, int style) {
+        styleBuffer[0] = style;
+        Font font = mFontLoader.getFont(family.mFamily, styleBuffer);
+        if (font != null) {
+            return new Typeface(family.mFamily, styleBuffer[0], font);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns one of the default typeface objects, based on the specified style
+     *
+     * @return the default typeface that corresponds to the style
+     */
+    public static Typeface defaultFromStyle(int style) {
+        return sDefaults[style];
+    }
+    
+    /**
+     * Create a new typeface from the specified font data.
+     * @param mgr The application's asset manager
+     * @param path  The file name of the font data in the assets directory
+     * @return The new typeface.
+     */
+    public static Typeface createFromAsset(AssetManager mgr, String path) {
+        return null;
+        //return new Typeface(nativeCreateFromAsset(mgr, path));
+    }
+    
+    // don't allow clients to call this directly
+    private Typeface(String family, int style, Font f) {
+        mFamily = family;
+        mFont = f;
+        mStyle = style;
+    }
+    
+    public static void init(FontLoader fontLoader) {
+        mFontLoader = fontLoader;
+        
+        DEFAULT = create(DEFAULT_FAMILY, NORMAL);
+        DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
+        SANS_SERIF = create("sans-serif", NORMAL);
+        SERIF = create("serif", NORMAL);
+        MONOSPACE = create("monospace", NORMAL);
+        sDefaults = new Typeface[] {
+                DEFAULT,
+                DEFAULT_BOLD,
+                create(DEFAULT_FAMILY, ITALIC),
+                create(DEFAULT_FAMILY, BOLD_ITALIC),
+        };
+        
+        /*
+        DEFAULT         = create((String)null, 0);
+        DEFAULT_BOLD    = create((String)null, Typeface.BOLD);
+        SANS_SERIF      = create("sans-serif", 0);
+        SERIF           = create("serif", 0);
+        MONOSPACE       = create("monospace", 0);
+        
+        sDefaults = new Typeface[] {
+            DEFAULT,
+            DEFAULT_BOLD,
+            create((String)null, Typeface.ITALIC),
+            create((String)null, Typeface.BOLD_ITALIC),
+        };*/
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath.java b/tools/layoutlib/bridge/src/android/util/FloatMath.java
new file mode 100644
index 0000000..aae44f2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Reimplements _Original_FloatMath with the standard libraries.
+ * 
+ * Math routines similar to those found in {@link java.lang.Math}. Performs
+ * computations on {@code float} values directly without incurring the overhead
+ * of conversions to and from {@code double}.
+ *
+ * <p>On one platform, {@code FloatMath.sqrt(100)} executes in one third of the
+ * time required by {@code java.lang.Math.sqrt(100)}.</p>
+ */
+public class FloatMath {
+
+    /** Prevents instantiation. */
+    private FloatMath() {}
+
+    /**
+     * Returns the float conversion of the most positive (i.e. closest to
+     * positive infinity) integer value which is less than the argument.
+     *
+     * @param value to be converted
+     * @return the floor of value
+     */
+    public static float floor(float value) {
+        return (float)Math.floor(value);
+    }
+
+    /**
+     * Returns the float conversion of the most negative (i.e. closest to
+     * negative infinity) integer value which is greater than the argument.
+     *
+     * @param value to be converted
+     * @return the ceiling of value
+     */
+    public static float ceil(float value) {
+        return (float)Math.ceil(value);
+    }
+
+    /**
+     * Returns the closest float approximation of the sine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the sine of angle
+     */
+    public static  float sin(float angle) {
+        return (float)Math.sin(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the cosine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the cosine of angle
+     */
+    public static float cos(float angle) {
+        return (float)Math.cos(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the square root of the
+     * argument.
+     *
+     * @param value to compute sqrt of
+     * @return the square root of value
+     */
+    public static float sqrt(float value) {
+        return (float)Math.sqrt(value);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
new file mode 100644
index 0000000..0910d79
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.BridgeContext;
+import com.android.layoutlib.bridge.BridgeXmlBlockParser;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import java.io.File;
+import java.io.FileReader;
+
+/**
+ * Custom implementation of {@link LayoutInflater} to handle custom views. 
+ */
+public final class BridgeInflater extends LayoutInflater {
+    
+    private final IProjectCallback mProjectCallback;
+
+    /**
+     * List of class prefixes which are tried first by default.
+     * <p/>
+     * This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
+     */ 
+    private static final String[] sClassPrefixList = {
+        "android.widget.",
+        "android.webkit."
+    };
+
+    protected BridgeInflater(LayoutInflater original, Context newContext) {
+        super(original, newContext);
+        mProjectCallback = null;
+    }
+    
+    /**
+     * Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
+     * 
+     * @param context The Android application context.
+     * @param projectCallback the {@link IProjectCallback} object.
+     */
+    public BridgeInflater(Context context, IProjectCallback projectCallback) {
+        super(context);
+        mProjectCallback = projectCallback;
+        mConstructorArgs[0] = context;
+    }
+
+    @Override
+    public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+        View view = null;
+
+        try {
+            // First try to find a class using the default Android prefixes
+            for (String prefix : sClassPrefixList) {
+                try {
+                    view = createView(name, prefix, attrs);
+                    if (view != null) {
+                        break;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // Ignore. We'll try again using the base class below.
+                }
+            }
+    
+            // Next try using the parent loader. This will most likely only work for
+            // fully-qualified class names.
+            try {
+                if (view == null) {
+                    view = super.onCreateView(name, attrs);
+                }
+            } catch (ClassNotFoundException e) {
+                // Ignore. We'll try again using the custom view loader below.
+            }
+    
+            // Finally try again using the custom view loader
+            try {
+                if (view == null) {
+                    view = loadCustomView(name, attrs);
+                }
+            } catch (ClassNotFoundException e) {
+                // If the class was not found, we throw the exception directly, because this
+                // method is already expected to throw it.
+                throw e;
+            }
+        } catch (Exception e) {
+            // Wrap the real exception in a ClassNotFoundException, so that the calling method
+            // can deal with it.
+            ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
+            throw exception;
+        }
+        
+        setupViewInContext(view, attrs);
+        
+        return view;
+    }
+    
+    @Override
+    public View createViewFromTag(String name, AttributeSet attrs) {
+        View view = null;
+        try {
+            view = super.createViewFromTag(name, attrs);
+        } catch (InflateException e) {
+            // try to load the class from using the custom view loader
+            try {
+                view = loadCustomView(name, attrs);
+            } catch (Exception e2) {
+                // Wrap the real exception in an InflateException so that the calling
+                // method can deal with it.
+                InflateException exception = new InflateException();
+                if (e2.getClass().equals(ClassNotFoundException.class) == false) { 
+                    exception.initCause(e2);
+                } else {
+                    exception.initCause(e);
+                }
+                throw exception;
+            }
+        }
+        
+        setupViewInContext(view, attrs);
+        
+        return view;
+    }
+    
+    @Override
+    public View inflate(int resource, ViewGroup root) {
+        Context context = getContext();
+        if (context instanceof BridgeContext) {
+            BridgeContext bridgeContext = (BridgeContext)context;
+            
+            IResourceValue value = null;
+
+            String[] layoutInfo = Bridge.resolveResourceValue(resource);
+            if (layoutInfo != null) {
+                value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT,
+                        layoutInfo[0]);
+            } else {
+                layoutInfo = mProjectCallback.resolveResourceValue(resource);
+                
+                if (layoutInfo != null) {
+                    value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
+                            layoutInfo[0]);
+                }
+            }
+
+            if (value != null) {
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        KXmlParser parser = new KXmlParser();
+                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                        parser.setInput(new FileReader(f));
+                        
+                        BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
+                                parser, bridgeContext, false);
+                        
+                        return inflate(bridgeParser, root);
+                    } catch (Exception e) {
+                        bridgeContext.getLogger().error(e);
+                        // return null below.
+                    }
+                }
+            }
+        }
+        return null;
+    }
+    
+    private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
+            Exception{
+        if (mProjectCallback != null) {
+            // first get the classname in case it's not the node name
+            if (name.equals("view")) {
+                name = attrs.getAttributeValue(null, "class");
+            }
+            
+            mConstructorArgs[1] = attrs;
+
+            Object customView = mProjectCallback.loadView(name, mConstructorSignature,
+                    mConstructorArgs);
+            
+            if (customView instanceof View) {
+                return (View)customView;
+            }
+        }
+
+        return null;
+    }
+    
+    
+    
+    private void setupViewInContext(View view, AttributeSet attrs) {
+        if (getContext() instanceof BridgeContext) {
+            BridgeContext bc = (BridgeContext) getContext();
+            if (attrs instanceof BridgeXmlBlockParser) {
+                Object viewKey = ((BridgeXmlBlockParser) attrs).getViewKey();
+                if (viewKey != null) {
+                    bc.addViewKey(view, viewKey);
+                }
+            }
+        }
+    }
+
+    @Override
+    public LayoutInflater cloneInContext(Context newContext) {
+        return new BridgeInflater(this, newContext);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
new file mode 100644
index 0000000..ce32da9
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+/**
+ * Mock version of the SurfaceView.
+ * Only non override public methods from the real SurfaceView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class SurfaceView extends MockView {
+
+    public SurfaceView(Context context) {
+        this(context, null);
+    }
+    
+    public SurfaceView(Context context, AttributeSet attrs) {
+        this(context, attrs , 0);
+    }
+
+    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    public SurfaceHolder getHolder() {
+        return mSurfaceHolder;
+    }
+
+    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+        
+        public boolean isCreating() {
+            return false;
+        }
+
+        public void addCallback(Callback callback) {
+        }
+
+        public void removeCallback(Callback callback) {
+        }
+        
+        public void setFixedSize(int width, int height) {
+        }
+
+        public void setSizeFromLayout() {
+        }
+
+        public void setFormat(int format) {
+        }
+
+        public void setType(int type) {
+        }
+
+        public void setKeepScreenOn(boolean screenOn) {
+        }
+        
+        public Canvas lockCanvas() {
+            return null;
+        }
+
+        public Canvas lockCanvas(Rect dirty) {
+            return null;
+        }
+
+        public void unlockCanvasAndPost(Canvas canvas) {
+        }
+
+        public Surface getSurface() {
+            return null;
+        }
+
+        public Rect getSurfaceFrame() {
+            return null;
+        }
+    };
+}
+
diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java
new file mode 100644
index 0000000..42e4dfa
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Picture;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Mock version of the WebView.
+ * Only non override public methods from the real WebView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class WebView extends MockView {
+
+    /**
+     * Construct a new WebView with a Context object.
+     * @param context A Context object used to access application assets.
+     */
+    public WebView(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     */
+    public WebView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.webViewStyle);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
+     */
+    public WebView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    // START FAKE PUBLIC METHODS
+    
+    public void setHorizontalScrollbarOverlay(boolean overlay) {
+    }
+
+    public void setVerticalScrollbarOverlay(boolean overlay) {
+    }
+
+    public boolean overlayHorizontalScrollbar() {
+        return false;
+    }
+
+    public boolean overlayVerticalScrollbar() {
+        return false;
+    }
+
+    public void savePassword(String host, String username, String password) {
+    }
+
+    public void setHttpAuthUsernamePassword(String host, String realm,
+            String username, String password) {
+    }
+
+    public String[] getHttpAuthUsernamePassword(String host, String realm) {
+        return null;
+    }
+
+    public void destroy() {
+    }
+
+    public static void enablePlatformNotifications() {
+    }
+
+    public static void disablePlatformNotifications() {
+    }
+
+    public WebBackForwardList saveState(Bundle outState) {
+        return null;
+    }
+
+    public WebBackForwardList restoreState(Bundle inState) {
+        return null;
+    }
+
+    public void loadUrl(String url) {
+    }
+
+    public void loadData(String data, String mimeType, String encoding) {
+    }
+
+    public void loadDataWithBaseURL(String baseUrl, String data,
+            String mimeType, String encoding, String failUrl) {
+    }
+
+    public void stopLoading() {
+    }
+
+    public void reload() {
+    }
+
+    public boolean canGoBack() {
+        return false;
+    }
+
+    public void goBack() {
+    }
+
+    public boolean canGoForward() {
+        return false;
+    }
+
+    public void goForward() {
+    }
+
+    public boolean canGoBackOrForward(int steps) {
+        return false;
+    }
+
+    public void goBackOrForward(int steps) {
+    }
+
+    public boolean pageUp(boolean top) {
+        return false;
+    }
+    
+    public boolean pageDown(boolean bottom) {
+        return false;
+    }
+
+    public void clearView() {
+    }
+    
+    public Picture capturePicture() {
+        return null;
+    }
+
+    public float getScale() {
+        return 0;
+    }
+
+    public void setInitialScale(int scaleInPercent) {
+    }
+
+    public void invokeZoomPicker() {
+    }
+
+    public void requestFocusNodeHref(Message hrefMsg) {
+    }
+
+    public void requestImageRef(Message msg) {
+    }
+
+    public String getUrl() {
+        return null;
+    }
+
+    public String getTitle() {
+        return null;
+    }
+
+    public Bitmap getFavicon() {
+        return null;
+    }
+
+    public int getProgress() {
+        return 0;
+    }
+    
+    public int getContentHeight() {
+        return 0;
+    }
+
+    public void pauseTimers() {
+    }
+
+    public void resumeTimers() {
+    }
+
+    public void clearCache() {
+    }
+
+    public void clearFormData() {
+    }
+
+    public void clearHistory() {
+    }
+
+    public void clearSslPreferences() {
+    }
+
+    public WebBackForwardList copyBackForwardList() {
+        return null;
+    }
+
+    public static String findAddress(String addr) {
+        return null;
+    }
+
+    public void documentHasImages(Message response) {
+    }
+
+    public void setWebViewClient(WebViewClient client) {
+    }
+
+    public void setDownloadListener(DownloadListener listener) {
+    }
+
+    public void setWebChromeClient(WebChromeClient client) {
+    }
+
+    public void addJavascriptInterface(Object obj, String interfaceName) {
+    }
+
+    public WebSettings getSettings() {
+        return null;
+    }
+
+    public static synchronized PluginList getPluginList() {
+        return null;
+    }
+
+    public void refreshPlugins(boolean reloadOpenPages) {
+    }
+
+    public View getZoomControls() {
+        return null;
+    }
+
+    public boolean zoomIn() {
+        return false;
+    }
+
+    public boolean zoomOut() {
+        return false;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
new file mode 100644
index 0000000..6abc452d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.internal.util.XmlUtils;
+import com.android.layoutlib.api.ILayoutBridge;
+import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
+import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
+import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
+import com.android.ninepatch.NinePatch;
+import com.android.tools.layoutlib.create.MethodAdapter;
+import com.android.tools.layoutlib.create.OverrideMethod;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.BridgeInflater;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.AttachInfo;
+import android.view.View.MeasureSpec;
+import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Main entry point of the LayoutLib Bridge.
+ * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
+ * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}.
+ */
+public final class Bridge implements ILayoutBridge {
+    
+    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
+    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
+    
+    public static class StaticMethodNotImplementedException extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+
+        public StaticMethodNotImplementedException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Maps from id to resource name/type.
+     */
+    private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
+    /**
+     * Same as sRMap except for int[] instead of int resources.
+     */
+    private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>();
+    /**
+     * Reverse map compared to sRMap, resource type -> (resource name -> id)
+     */
+    private final static Map<String, Map<String, Integer>> sRFullMap =
+        new HashMap<String, Map<String,Integer>>();
+    
+    private final static Map<Object, Map<String, Bitmap>> sProjectBitmapCache =
+        new HashMap<Object, Map<String, Bitmap>>();
+    private final static Map<Object, Map<String, NinePatch>> sProject9PatchCache =
+        new HashMap<Object, Map<String, NinePatch>>();
+
+    private final static Map<String, Bitmap> sFrameworkBitmapCache = new HashMap<String, Bitmap>();
+    private final static Map<String, NinePatch> sFramework9PatchCache =
+        new HashMap<String, NinePatch>();
+    
+    private static Map<String, Map<String, Integer>> sEnumValueMap;
+
+    /**
+     * A default logger than prints to stdout/stderr.
+     */
+    private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
+        public void error(String message) {
+            System.err.println(message);
+        }
+
+        public void error(Throwable t) {
+            String message = t.getMessage();
+            if (message == null) {
+                message = t.getClass().getName();
+            }
+
+            System.err.println(message);
+        }
+
+        public void warning(String message) {
+            System.out.println(message);
+        }
+    };
+
+    /**
+     * Logger defined during a compute layout operation.
+     * <p/>
+     * This logger is generally set to {@link #sDefaultLogger} except during rendering
+     * operations when it might be set to a specific provided logger.
+     * <p/>
+     * To change this value, use a block synchronized on {@link #sDefaultLogger}.
+     */
+    private static ILayoutLog sLogger = sDefaultLogger;
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel()
+     */
+    public int getApiLevel() {
+        return API_CURRENT;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map)
+     */
+    public boolean init(
+            String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
+
+        return sinit(fontOsLocation, enumValueMap);
+    }
+    
+    private static synchronized boolean sinit(String fontOsLocation,
+            Map<String, Map<String, Integer>> enumValueMap) {
+
+        // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
+        // on static (native) methods which prints the signature on the console and
+        // throws an exception.
+        // This is useful when testing the rendering in ADT to identify static native 
+        // methods that are ignored -- layoutlib_create makes them returns 0/false/null
+        // which is generally OK yet might be a problem, so this is how you'd find out.
+        //
+        // Currently layoutlib_create only overrides static native method.
+        // Static non-natives are not overridden and thus do not get here.
+        final String debug = System.getenv("DEBUG_LAYOUT");
+        if (debug != null && !debug.equals("0") && !debug.equals("false")) {
+
+            OverrideMethod.setDefaultListener(new MethodAdapter() {
+                @Override
+                public void onInvokeV(String signature, boolean isNative, Object caller) {
+                    if (sLogger != null) {
+                        synchronized (sDefaultLogger) {
+                            sLogger.error("Missing Stub: " + signature +
+                                    (isNative ? " (native)" : ""));
+                        }
+                    }
+
+                    if (debug.equalsIgnoreCase("throw")) {
+                        // Throwing this exception doesn't seem that useful. It breaks
+                        // the layout editor yet doesn't display anything meaningful to the
+                        // user. Having the error in the console is just as useful. We'll
+                        // throw it only if the environment variable is "throw" or "THROW".
+                        throw new StaticMethodNotImplementedException(signature);
+                    }
+                }
+            });
+        }
+
+        // Override View.isInEditMode to return true.
+        //
+        // This allows custom views that are drawn in the Graphical Layout Editor to adapt their
+        // rendering for preview. Most important this let custom views know that they can't expect
+        // the rest of their activities to be alive.
+        OverrideMethod.setMethodListener("android.view.View#isInEditMode()Z",
+            new MethodAdapter() {
+                @Override
+                public int onInvokeI(String signature, boolean isNative, Object caller) {
+                    return 1;
+                }
+            }
+        );
+
+        // load the fonts.
+        FontLoader fontLoader = FontLoader.create(fontOsLocation);
+        if (fontLoader != null) {
+            Typeface.init(fontLoader);
+        } else {
+            return false;
+        }
+        
+        sEnumValueMap = enumValueMap;
+
+        // now parse com.android.internal.R (and only this one as android.R is a subset of
+        // the internal version), and put the content in the maps.
+        try {
+            // WARNING: this only works because the class is already loaded, and therefore
+            // the objects returned by Field.get() are the same as the ones used by
+            // the code accessing the R class.
+            // int[] does not implement equals/hashCode, and if the parsing used a different class
+            // loader for the R class, this would NOT work.
+            Class<?> r = com.android.internal.R.class;
+            
+            for (Class<?> inner : r.getDeclaredClasses()) {
+                String resType = inner.getSimpleName();
+
+                Map<String, Integer> fullMap = new HashMap<String, Integer>();
+                sRFullMap.put(resType, fullMap);
+                
+                for (Field f : inner.getDeclaredFields()) {
+                    // only process static final fields. Since the final attribute may have
+                    // been altered by layoutlib_create, we only check static
+                    int modifiers = f.getModifiers();
+                    if (Modifier.isStatic(modifiers)) {
+                        Class<?> type = f.getType();
+                        if (type.isArray() && type.getComponentType() == int.class) {
+                            // if the object is an int[] we put it in sRArrayMap
+                            sRArrayMap.put((int[]) f.get(null), f.getName());
+                        } else if (type == int.class) {
+                            Integer value = (Integer) f.get(null); 
+                            sRMap.put(value, new String[] { f.getName(), resType });
+                            fullMap.put(f.getName(), value);
+                        } else {
+                            assert false;
+                        }
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            // FIXME: log/return the error (there's no logger object at this point!)
+            e.printStackTrace();
+            return false;
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+            return false;
+        }
+
+        return true;
+    }
+
+    /*
+     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+     */
+    @Deprecated
+    public ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+            Object projectKey,
+            int screenWidth, int screenHeight, String themeName,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback customViewLoader, ILayoutLog logger) {
+        boolean isProjectTheme = false;
+        if (themeName.charAt(0) == '*') {
+            themeName = themeName.substring(1);
+            isProjectTheme = true;
+        }
+        
+        return computeLayout(layoutDescription, projectKey,
+                screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
+                DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+                themeName, isProjectTheme,
+                projectResources, frameworkResources, customViewLoader, logger);
+    }
+
+    /*
+     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+     */
+    public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
+            int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback customViewLoader, ILayoutLog logger) {
+        return computeLayout(layoutDescription, projectKey,
+                screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
+                DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+                themeName, isProjectTheme,
+                projectResources, frameworkResources, customViewLoader, logger);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+     */
+    public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
+            int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
+            String themeName, boolean isProjectTheme,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            IProjectCallback customViewLoader, ILayoutLog logger) {
+        if (logger == null) {
+            logger = sDefaultLogger;
+        }
+        
+        synchronized (sDefaultLogger) {
+            sLogger = logger;
+        }
+
+        // find the current theme and compute the style inheritance map
+        Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
+            new HashMap<IStyleResourceValue, IStyleResourceValue>();
+        
+        IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme,
+                projectResources.get(BridgeConstants.RES_STYLE),
+                frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap);
+        
+        BridgeContext context = null; 
+        try {
+            // setup the display Metrics.
+            DisplayMetrics metrics = new DisplayMetrics();
+            metrics.density = density / (float) DisplayMetrics.DEFAULT_DENSITY;
+            metrics.scaledDensity = metrics.density;
+            metrics.widthPixels = screenWidth;
+            metrics.heightPixels = screenHeight;
+            metrics.xdpi = xdpi;
+            metrics.ydpi = ydpi;
+
+            context = new BridgeContext(projectKey, metrics, currentTheme, projectResources,
+                    frameworkResources, styleParentMap, customViewLoader, logger);
+            BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
+            context.setBridgeInflater(inflater);
+            
+            IResourceValue windowBackground = null;
+            int screenOffset = 0;
+            if (currentTheme != null) {
+                windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
+                windowBackground = context.resolveResValue(windowBackground);
+    
+                screenOffset = getScreenOffset(currentTheme, context);
+            }
+            
+            // we need to make sure the Looper has been initialized for this thread.
+            // this is required for View that creates Handler objects.
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            
+            BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
+                    context, false /* platformResourceFlag */);
+            
+            ViewGroup root = new FrameLayout(context);
+        
+            View view = inflater.inflate(parser, root);
+            
+            // set the AttachInfo on the root view.
+            AttachInfo info = new AttachInfo(new WindowSession(), new Window(),
+                    new Handler(), null);
+            info.mHasWindowFocus = true;
+            info.mWindowVisibility = View.VISIBLE;
+            info.mInTouchMode = false; // this is so that we can display selections.
+            root.dispatchAttachedToWindow(info, 0);
+
+            // get the background drawable
+            if (windowBackground != null) {
+                Drawable d = ResourceHelper.getDrawable(windowBackground.getValue(),
+                        context, true /* isFramework */);
+                root.setBackgroundDrawable(d);
+            }
+
+            int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
+            int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
+                    MeasureSpec.EXACTLY);
+
+            // measure the views
+            view.measure(w_spec, h_spec);
+            view.layout(0, screenOffset, screenWidth, screenHeight);
+            
+            // draw them
+            BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset,
+                    logger);
+            
+            root.draw(canvas);
+            canvas.dispose();
+            
+            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
+                    canvas.getImage());
+        } catch (Throwable e) {
+            // get the real cause of the exception.
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = t.getCause();
+            }
+
+            // log it
+            logger.error(t);
+
+            // then return with an ERROR status and the message from the real exception
+            return new LayoutResult(ILayoutResult.ERROR,
+                    t.getClass().getSimpleName() + ": " + t.getMessage());
+        } finally {
+            // Make sure to remove static references, otherwise we could not unload the lib
+            BridgeResources.clearSystem();
+            BridgeAssetManager.clearSystem();
+            
+            // Remove the global logger
+            synchronized (sDefaultLogger) {
+                sLogger = sDefaultLogger;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object)
+     */
+    public void clearCaches(Object projectKey) {
+        if (projectKey != null) {
+            sProjectBitmapCache.remove(projectKey);
+            sProject9PatchCache.remove(projectKey);
+        }
+    }
+    
+    /**
+     * Returns details of a framework resource from its integer value.
+     * @param value the integer value
+     * @return an array of 2 strings containing the resource name and type, or null if the id
+     * does not match any resource. 
+     */
+    public static String[] resolveResourceValue(int value) {
+        return sRMap.get(value);
+        
+    }
+    
+    /**
+     * Returns the name of a framework resource whose value is an int array.
+     * @param array
+     */
+    public static String resolveResourceValue(int[] array) {
+        return sRArrayMap.get(array);
+    }
+    
+    /**
+     * Returns the integer id of a framework resource, from a given resource type and resource name.
+     * @param type the type of the resource
+     * @param name the name of the resource.
+     * @return an {@link Integer} containing the resource id, or null if no resource were found.
+     */
+    public static Integer getResourceValue(String type, String name) {
+        Map<String, Integer> map = sRFullMap.get(type);
+        if (map != null) {
+            return map.get(name);
+        }
+        
+        return null;
+    }
+    
+    static Map<String, Integer> getEnumValues(String attributeName) {
+        if (sEnumValueMap != null) {
+            return sEnumValueMap.get(attributeName);
+        }
+        
+        return null;
+    }
+
+    /**
+     * Visits a View and its children and generate a {@link ILayoutViewInfo} containing the
+     * bounds of all the views.
+     * @param view the root View
+     * @param context the context.
+     */
+    private ILayoutViewInfo visit(View view, BridgeContext context) {
+        if (view == null) {
+            return null;
+        }
+
+        LayoutViewInfo result = new LayoutViewInfo(view.getClass().getName(),
+                context.getViewKey(view),
+                view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+
+        if (view instanceof ViewGroup) {
+            ViewGroup group = ((ViewGroup) view);
+            int n = group.getChildCount();
+            ILayoutViewInfo[] children = new ILayoutViewInfo[n];
+            for (int i = 0; i < group.getChildCount(); i++) {
+                children[i] = visit(group.getChildAt(i), context);
+            }
+            result.setChildren(children);
+        }
+
+        return result;
+    }
+    
+    /**
+     * Compute style information from the given list of style for the project and framework.
+     * @param themeName the name of the current theme.  In order to differentiate project and
+     * platform themes sharing the same name, all project themes must be prepended with
+     * a '*' character.
+     * @param isProjectTheme Is this a project theme 
+     * @param inProjectStyleMap the project style map
+     * @param inFrameworkStyleMap the framework style map
+     * @param outInheritanceMap the map of style inheritance. This is filled by the method
+     * @return the {@link IStyleResourceValue} matching <var>themeName</var>
+     */
+    private IStyleResourceValue computeStyleMaps(
+            String themeName, boolean isProjectTheme, Map<String,
+            IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
+            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
+        
+        if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
+            // first, get the theme
+            IResourceValue theme = null;
+            
+            // project theme names have been prepended with a *
+            if (isProjectTheme) {
+                theme = inProjectStyleMap.get(themeName);
+            } else {
+                theme = inFrameworkStyleMap.get(themeName);
+            }
+            
+            if (theme instanceof IStyleResourceValue) {
+                // compute the inheritance map for both the project and framework styles
+                computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
+                        inFrameworkStyleMap, outInheritanceMap);
+    
+                // Compute the style inheritance for the framework styles/themes.
+                // Since, for those, the style parent values do not contain 'android:'
+                // we want to force looking in the framework style only to avoid using
+                // similarly named styles from the project.
+                // To do this, we pass null in lieu of the project style map.
+                computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
+                        inFrameworkStyleMap, outInheritanceMap);
+    
+                return (IStyleResourceValue)theme;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * Compute the parent style for all the styles in a given list.
+     * @param styles the styles for which we compute the parent.
+     * @param inProjectStyleMap the map of project styles.
+     * @param inFrameworkStyleMap the map of framework styles.
+     * @param outInheritanceMap the map of style inheritance. This is filled by the method.
+     */
+    private void computeStyleInheritance(Collection<IResourceValue> styles,
+            Map<String, IResourceValue> inProjectStyleMap,
+            Map<String, IResourceValue> inFrameworkStyleMap,
+            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
+        for (IResourceValue value : styles) {
+            if (value instanceof IStyleResourceValue) {
+                IStyleResourceValue style = (IStyleResourceValue)value;
+                IStyleResourceValue parentStyle = null;
+
+                // first look for a specified parent.
+                String parentName = style.getParentStyle();
+                
+                // no specified parent? try to infer it from the name of the style.
+                if (parentName == null) {
+                    parentName = getParentName(value.getName());
+                }
+
+                if (parentName != null) {
+                    parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
+                    
+                    if (parentStyle != null) {
+                        outInheritanceMap.put(style, parentStyle);
+                    }
+                }
+            }
+        }
+    }
+    
+    /**
+     * Searches for and returns the {@link IStyleResourceValue} from a given name.
+     * <p/>The format of the name can be:
+     * <ul>
+     * <li>[android:]&lt;name&gt;</li>
+     * <li>[android:]style/&lt;name&gt;</li>
+     * <li>@[android:]style/&lt;name&gt;</li>
+     * </ul>
+     * @param parentName the name of the style.
+     * @param inProjectStyleMap the project style map. Can be <code>null</code>
+     * @param inFrameworkStyleMap the framework style map.
+     * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found.
+     */
+    private IStyleResourceValue getStyle(String parentName,
+            Map<String, IResourceValue> inProjectStyleMap,
+            Map<String, IResourceValue> inFrameworkStyleMap) {
+        boolean frameworkOnly = false;
+        
+        String name = parentName;
+        
+        // remove the useless @ if it's there
+        if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
+            name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
+        }
+        
+        // check for framework identifier.
+        if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
+            frameworkOnly = true;
+            name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
+        }
+        
+        // at this point we could have the format style/<name>. we want only the name
+        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
+            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
+        }
+
+        IResourceValue parent = null;
+        
+        // if allowed, search in the project resources.
+        if (frameworkOnly == false && inProjectStyleMap != null) {
+            parent = inProjectStyleMap.get(name);
+        }
+
+        // if not found, then look in the framework resources.
+        if (parent == null) {
+            parent = inFrameworkStyleMap.get(name);
+        }
+        
+        // make sure the result is the proper class type and return it.
+        if (parent instanceof IStyleResourceValue) {
+            return (IStyleResourceValue)parent;
+        }
+        
+        sLogger.error(String.format("Unable to resolve parent style name: ", parentName));
+        
+        return null;
+    }
+    
+    /**
+     * Computes the name of the parent style, or <code>null</code> if the style is a root style.
+     */
+    private String getParentName(String styleName) {
+        int index = styleName.lastIndexOf('.');
+        if (index != -1) {
+            return styleName.substring(0, index);
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Returns the top screen offset. This depends on whether the current theme defines the user
+     * of the title and status bars.
+     * @return the pixel height offset
+     */
+    private int getScreenOffset(IStyleResourceValue currentTheme, BridgeContext context) {
+        int offset = 0;
+
+        // get the title bar flag from the current theme.
+        IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
+        
+        // because it may reference something else, we resolve it.
+        value = context.resolveResValue(value);
+
+        // if there's a value and it's true (default is false)
+        if (value == null || value.getValue() == null ||
+                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
+            // get value from the theme.
+            value = context.findItemInStyle(currentTheme, "windowTitleSize");
+            
+            // resolve it
+            value = context.resolveResValue(value);
+            
+            // default value
+            offset = DEFAULT_TITLE_BAR_HEIGHT;
+
+            // get the real value;
+            if (value != null) {
+                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                if (typedValue != null) {
+                    offset = (int)typedValue.getDimension(context.getResources().mMetrics);   
+                }
+            }
+        }
+        
+        // get the fullscreen flag from the current theme.
+        value = context.findItemInStyle(currentTheme, "windowFullscreen");
+        
+        // because it may reference something else, we resolve it.
+        value = context.resolveResValue(value);
+        
+        if (value == null || value.getValue() == null ||
+                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
+            // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it.
+            offset += DEFAULT_STATUS_BAR_HEIGHT;
+        }
+
+        return offset;
+    }
+
+    /**
+     * Returns the bitmap for a specific path, from a specific project cache, or from the
+     * framework cache.
+     * @param value the path of the bitmap
+     * @param projectKey the key of the project, or null to query the framework cache.
+     * @return the cached Bitmap or null if not found.
+     */
+    static Bitmap getCachedBitmap(String value, Object projectKey) {
+        if (projectKey != null) {
+            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+            if (map != null) {
+                return map.get(value);
+            }
+            
+            return null;
+        }
+        
+        return sFrameworkBitmapCache.get(value);
+    }
+
+    /**
+     * Sets a bitmap in a project cache or in the framework cache.
+     * @param value the path of the bitmap
+     * @param bmp the Bitmap object
+     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
+     */
+    static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
+        if (projectKey != null) {
+            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+
+            if (map == null) {
+                map = new HashMap<String, Bitmap>();
+                sProjectBitmapCache.put(projectKey, map);
+            }
+            
+            map.put(value, bmp);
+        }
+        
+        sFrameworkBitmapCache.put(value, bmp);
+    }
+
+    /**
+     * Returns the 9 patch for a specific path, from a specific project cache, or from the
+     * framework cache.
+     * @param value the path of the 9 patch
+     * @param projectKey the key of the project, or null to query the framework cache.
+     * @return the cached 9 patch or null if not found.
+     */
+    static NinePatch getCached9Patch(String value, Object projectKey) {
+        if (projectKey != null) {
+            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+            
+            if (map != null) {
+                return map.get(value);
+            }
+            
+            return null;
+        }
+        
+        return sFramework9PatchCache.get(value);
+    }
+
+    /**
+     * Sets a 9 patch in a project cache or in the framework cache.
+     * @param value the path of the 9 patch
+     * @param ninePatch the 9 patch object
+     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
+     */
+    static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
+        if (projectKey != null) {
+            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+
+            if (map == null) {
+                map = new HashMap<String, NinePatch>();
+                sProject9PatchCache.put(projectKey, map);
+            }
+            
+            map.put(value, ninePatch);
+        }
+        
+        sFramework9PatchCache.put(value, ninePatch);
+    }
+    
+    /**
+     * Implementation of {@link IWindowSession} so that mSession is not null in
+     * the {@link SurfaceView}.
+     */
+    private static final class WindowSession implements IWindowSession {
+
+        @SuppressWarnings("unused")
+        public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
+                throws RemoteException {
+            // pass for now.
+            return 0;
+        }
+
+        @SuppressWarnings("unused")
+        public void finishDrawing(IWindow arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void finishKey(IWindow arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public boolean getInTouchMode() throws RemoteException {
+            // pass for now.
+            return false;
+        }
+
+        @SuppressWarnings("unused")
+        public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
+            // pass for now.
+            return false;
+        }
+        
+        @SuppressWarnings("unused")
+        public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
+            // pass for now.
+            return null;
+        }
+
+        @SuppressWarnings("unused")
+        public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
+            // pass for now.
+            return null;
+        }
+
+        @SuppressWarnings("unused")
+        public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4,
+                boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Surface arg8)
+                throws RemoteException {
+            // pass for now.
+            return 0;
+        }
+
+        public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
+            // pass for now.
+        }
+        
+        @SuppressWarnings("unused")
+        public void remove(IWindow arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void setInTouchMode(boolean arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
+            // pass for now.
+        }
+
+        public void setInsets(IWindow window, int touchable, Rect contentInsets,
+                Rect visibleInsets) {
+            // pass for now.
+        }
+        
+        public IBinder asBinder() {
+            // pass for now.
+            return null;
+        }
+    }
+    
+    /**
+     * Implementation of {@link IWindow} to pass to the {@link AttachInfo}.
+     */
+    private static final class Window implements IWindow {
+
+        @SuppressWarnings("unused")
+        public void dispatchAppVisibility(boolean arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void dispatchGetNewSurface() throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void dispatchKey(KeyEvent arg0) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
+                throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4)
+                throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
+            // pass for now.
+        }
+
+        public IBinder asBinder() {
+            // pass for now.
+            return null;
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
new file mode 100644
index 0000000..1fa11af
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+
+import java.util.Locale;
+
+public class BridgeAssetManager extends AssetManager {
+    
+    /**
+     * This initializes the static field {@link AssetManager#mSystem} which is used
+     * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
+     * <p/>
+     * They will end up using our bridge asset manager.
+     * <p/>
+     * {@link Bridge} calls this method after setting up a new bridge.
+     */
+    /*package*/ static AssetManager initSystem() {
+        if (!(AssetManager.mSystem instanceof BridgeAssetManager)) {
+            // Note that AssetManager() creates a system AssetManager and we override it
+            // with our BridgeAssetManager.
+            AssetManager.mSystem = new BridgeAssetManager();
+            AssetManager.mSystem.makeStringBlocks(false);
+        }
+        return AssetManager.mSystem;
+    }
+    
+    /**
+     * Clears the static {@link AssetManager#mSystem} to make sure we don't leave objects
+     * around that would prevent us from unloading the library.
+     */
+    /*package*/ static void clearSystem() {
+        AssetManager.mSystem = null;
+    }
+    
+    private BridgeAssetManager() {
+    }
+    
+    /**
+     * Change the configuration used when retrieving resources.  Not for use by applications.
+     */
+    @Override
+    public void setConfiguration(int mcc, int mnc, String locale,
+            int orientation, int touchscreen, int density, int keyboard,
+            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+            int version)  {
+        
+        Configuration c = new Configuration();
+        c.mcc = mcc;
+        c.mnc = mnc;
+        c.locale = new Locale(locale);
+        c.touchscreen = touchscreen;
+        c.keyboard = keyboard;
+        c.keyboardHidden = keyboardHidden;
+        c.navigation = navigation;
+        c.orientation = orientation;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java
new file mode 100644
index 0000000..70c26a7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.ILayoutLog;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.DrawFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Picture;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Xfermode;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Region.Op;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.util.Stack;
+
+import javax.microedition.khronos.opengles.GL;
+
+/**
+ * Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
+ */
+public class BridgeCanvas extends Canvas {
+    
+    private BufferedImage mBufferedImage;
+    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
+    private final ILayoutLog mLogger;
+
+    public BridgeCanvas(int width, int height, ILayoutLog logger) {
+        mLogger = logger;
+        mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        mGraphicsStack.push(mBufferedImage.createGraphics());
+    }
+    
+    public BridgeCanvas(int width, int height) {
+        this(width, height, null /* logger*/);
+    }
+    
+    public BufferedImage getImage() {
+        return mBufferedImage;
+    }
+    
+    Graphics2D getGraphics2d() {
+        return mGraphicsStack.peek();
+    }
+    
+    void dispose() {
+        while (mGraphicsStack.size() > 0) {
+            mGraphicsStack.pop().dispose();
+        }
+    }
+    
+    /**
+     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
+     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
+     */
+    private Graphics2D getNewGraphics(Paint paint, Graphics2D g) {
+        // make new one
+        g = (Graphics2D)g.create();
+        g.setColor(new Color(paint.getColor()));
+        int alpha = paint.getAlpha();
+        float falpha = alpha / 255.f;
+        
+        Xfermode xfermode = paint.getXfermode();
+        if (xfermode instanceof PorterDuffXfermode) {
+            PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
+            
+            setModeInGraphics(mode, g, falpha);
+        } else {
+            if (mLogger != null && xfermode != null) {
+                mLogger.warning(String.format(
+                        "Xfermode '%1$s' is not supported in the Layout Editor.",
+                        xfermode.getClass().getCanonicalName()));
+            }
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+        }
+        
+        Shader shader = paint.getShader();
+        if (shader instanceof LinearGradient) {
+            g.setPaint(((LinearGradient)shader).getPaint());
+        } else {
+            if (mLogger != null && shader != null) {
+                mLogger.warning(String.format(
+                        "Shader '%1$s' is not supported in the Layout Editor.",
+                        shader.getClass().getCanonicalName()));
+            }
+        }
+        
+        return g;
+    }
+    
+    private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) {
+        switch (mode) {
+            case CLEAR:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
+                break;
+            case DARKEN:
+                break;
+            case DST:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
+                break;
+            case DST_ATOP:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
+                break;
+            case DST_IN:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
+                break;
+            case DST_OUT:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
+                break;
+            case DST_OVER:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
+                break;
+            case LIGHTEN:
+                break;
+            case MULTIPLY:
+                break;
+            case SCREEN:
+                break;
+            case SRC:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
+                break;
+            case SRC_ATOP:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
+                break;
+            case SRC_IN:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
+                break;
+            case SRC_OUT:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
+                break;
+            case SRC_OVER:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+                break;
+            case XOR:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
+                break;
+        }
+    }
+    
+    // --------------------
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#translate(float, float)
+     */
+    @Override
+    public void translate(float dx, float dy) {
+        getGraphics2d().translate(dx, dy);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#save()
+     */
+    @Override
+    public int save() {
+        Graphics2D g = (Graphics2D)getGraphics2d().create();
+        mGraphicsStack.push(g);
+        
+        return mGraphicsStack.size() - 1;
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#save(int)
+     */
+    @Override
+    public int save(int saveFlags) {
+        // For now we ignore saveFlags
+        return save();
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#restore()
+     */
+    @Override
+    public void restore() {
+        mGraphicsStack.pop();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#restoreToCount(int)
+     */
+    @Override
+    public void restoreToCount(int saveCount) {
+        while (mGraphicsStack.size() > saveCount) {
+            mGraphicsStack.pop();
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getSaveCount()
+     */
+    @Override
+    public int getSaveCount() {
+        return mGraphicsStack.size() - 1;
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
+     */
+    @Override
+    public boolean clipRect(float left, float top, float right, float bottom, Op op) {
+        return clipRect(left, top, right, bottom);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(float, float, float, float)
+     */
+    @Override
+    public boolean clipRect(float left, float top, float right, float bottom) {
+        getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(int, int, int, int)
+     */
+    @Override
+    public boolean clipRect(int left, int top, int right, int bottom) {
+        getGraphics2d().clipRect(left, top, right-left, bottom-top);
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(android.graphics.Rect, android.graphics.Region.Op)
+     */
+    @Override
+    public boolean clipRect(Rect rect, Op op) {
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(android.graphics.Rect)
+     */
+    @Override
+    public boolean clipRect(Rect rect) {
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(android.graphics.RectF, android.graphics.Region.Op)
+     */
+    @Override
+    public boolean clipRect(RectF rect, Op op) {
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRect(android.graphics.RectF)
+     */
+    @Override
+    public boolean clipRect(RectF rect) {
+        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    }
+    
+    @Override
+    public boolean quickReject(RectF rect, EdgeType type) {
+        return false;
+    }
+
+    @Override
+    public boolean quickReject(Path path, EdgeType type) {
+        return false;
+    }
+
+    @Override
+    public boolean quickReject(float left, float top, float right, float bottom,
+                               EdgeType type) {
+        return false;
+    }
+
+    /**
+     * Retrieve the clip bounds, returning true if they are non-empty.
+     *
+     * @param bounds Return the clip bounds here. If it is null, ignore it but
+     *               still return true if the current clip is non-empty.
+     * @return true if the current clip is non-empty.
+     */
+    @Override
+    public boolean getClipBounds(Rect bounds) {
+        Rectangle rect = getGraphics2d().getClipBounds();
+        if (rect != null) {
+            bounds.left = rect.x;
+            bounds.top = rect.y;
+            bounds.right = rect.x + rect.width;
+            bounds.bottom = rect.y + rect.height;
+            return true;
+        }
+        return false;
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode)
+     */
+    @Override
+    public void drawColor(int color, PorterDuff.Mode mode) {
+        Graphics2D g = getGraphics2d();
+        
+        // save old color
+        Color c = g.getColor();
+        
+        Composite composite = g.getComposite();
+        
+        // get the alpha from the color
+        int alpha = color >>> 24;
+        float falpha = alpha / 255.f;
+        
+        setModeInGraphics(mode, g, falpha);
+        
+        g.setColor(new Color(color));
+        
+        getGraphics2d().fillRect(0, 0, getWidth(), getHeight());
+        
+        g.setComposite(composite);
+        
+        // restore color
+        g.setColor(c);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawColor(int)
+     */
+    @Override
+    public void drawColor(int color) {
+        drawColor(color, PorterDuff.Mode.SRC_OVER);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawARGB(int, int, int, int)
+     */
+    @Override
+    public void drawARGB(int a, int r, int g, int b) {
+        drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawRGB(int, int, int)
+     */
+    @Override
+    public void drawRGB(int r, int g, int b) {
+        drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
+    }
+
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getWidth()
+     */
+    @Override
+    public int getWidth() {
+        return mBufferedImage.getWidth();
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getHeight()
+     */
+    @Override
+    public int getHeight() {
+        return mBufferedImage.getHeight();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPaint(android.graphics.Paint)
+     */
+    @Override
+    public void drawPaint(Paint paint) {
+        drawColor(paint.getColor());
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+        drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
+                (int)left, (int)top,
+                (int)left+bitmap.getWidth(), (int)top+bitmap.getHeight(), paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+        if (src == null) {
+            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
+                    dst.left, dst.top, dst.right, dst.bottom, paint);
+        } else {
+            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
+                    dst.left, dst.top, dst.right, dst.bottom, paint);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+        if (src == null) {
+            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
+        } else {
+            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
+            int height, boolean hasAlpha, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+    
+    private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft,
+            int dtop, int dright, int dbottom, Paint paint) {
+        BufferedImage image = bitmap.getImage();
+        
+        Graphics2D g = getGraphics2d();
+        
+        Composite c = null;
+        
+        if (paint.isFilterBitmap()) {
+            g = (Graphics2D)g.create();
+            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        }
+        
+        if (paint.getAlpha() != 0xFF) {
+            c = g.getComposite();
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+                    paint.getAlpha()/255.f));
+        }
+        
+        g.drawImage(image, dleft, dtop, dright, dbottom,
+                sleft, stop, sright, sbottom, null);
+
+        if (paint.isFilterBitmap()) {
+            g.dispose();
+        }
+        
+        if (c != null) {
+            g.setComposite(c);
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#rotate(float, float, float)
+     */
+    @Override
+    public void rotate(float degrees, float px, float py) {
+        if (degrees != 0) {
+            Graphics2D g = getGraphics2d();
+            g.translate(px, py);
+            g.rotate(Math.toRadians(degrees));
+            g.translate(-px, -py);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#rotate(float)
+     */
+    @Override
+    public void rotate(float degrees) {
+        getGraphics2d().rotate(Math.toRadians(degrees));
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#scale(float, float, float, float)
+     */
+    @Override
+    public void scale(float sx, float sy, float px, float py) {
+        Graphics2D g = getGraphics2d();
+        g.translate(px, py);
+        g.scale(sx, sy);
+        g.translate(-px, -py);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#scale(float, float)
+     */
+    @Override
+    public void scale(float sx, float sy) {
+        getGraphics2d().scale(sx, sy);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
+        Graphics2D g = getGraphics2d();
+        
+        g = (Graphics2D)g.create();
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        g.setFont(paint.getFont());
+        
+        // set the color. because this only handles RGB we have to handle the alpha separately
+        g.setColor(new Color(paint.getColor()));
+        int alpha = paint.getAlpha();
+        float falpha = alpha / 255.f;
+        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+
+        // Paint.TextAlign indicates how the text is positioned relative to X.
+        // LEFT is the default and there's nothing to do.
+        if (paint.getTextAlign() != Align.LEFT) {
+            float m = paint.measureText(text, index, count);
+            if (paint.getTextAlign() == Align.CENTER) {
+                x -= m / 2;
+            } else if (paint.getTextAlign() == Align.RIGHT) {
+                x -= m;
+            }
+        }
+        
+        g.drawChars(text, index, count, (int)x, (int)y);
+        
+        g.dispose();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
+        drawText(text.toString().toCharArray(), start, end - start, x, y, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawText(java.lang.String, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawText(String text, float x, float y, Paint paint) {
+        drawText(text.toCharArray(), 0, text.length(), x, y, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawText(java.lang.String, int, int, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
+        drawText(text.toCharArray(), start, end - start, x, y, paint);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint)
+     */
+    @Override
+    public void drawRect(RectF rect, Paint paint) {
+        doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+        doDrawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top), paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawRect(android.graphics.Rect, android.graphics.Paint)
+     */
+    @Override
+    public void drawRect(Rect r, Paint paint) {
+        doDrawRect(r.left, r.top, r.width(), r.height(), paint);
+    }
+
+    private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        Style style = paint.getStyle();
+        
+        // draw
+        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+            g.fillRect(left, top, width, height);
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.drawRect(left, top, width, height);
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        Style style = paint.getStyle();
+        
+        // draw
+        
+        int arcWidth = (int)(rx * 2);
+        int arcHeight = (int)(ry * 2);
+        
+        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+            g.fillRoundRect((int)rect.left, (int)rect.right, (int)rect.width(), (int)rect.height(),
+                    arcWidth, arcHeight);
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.drawRoundRect((int)rect.left, (int)rect.right, (int)rect.width(), (int)rect.height(),
+                    arcWidth, arcHeight);
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawLine(float, float, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint)
+     */
+    @Override
+    public void drawLines(float[] pts, int offset, int count, Paint paint) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        for (int i = 0 ; i < count ; i += 4) {
+            g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
+                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawLines(float[], android.graphics.Paint)
+     */
+    @Override
+    public void drawLines(float[] pts, Paint paint) {
+        drawLines(pts, 0, pts.length, paint);
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawCircle(float cx, float cy, float radius, Paint paint) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        Style style = paint.getStyle();
+        
+        int size = (int)(radius * 2);
+
+        // draw
+        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+            g.fillOval((int)(cx - radius), (int)(cy - radius), size, size);
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.drawOval((int)(cx - radius), (int)(cy - radius), size, size);
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawOval(android.graphics.RectF, android.graphics.Paint)
+     */
+    @Override
+    public void drawOval(RectF oval, Paint paint) {
+        // get current graphics
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        Style style = paint.getStyle();
+        
+        // draw
+        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+            g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint)
+     */
+    @Override
+    public void drawPath(Path path, Paint paint) {
+        // get current graphics
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        Style style = paint.getStyle();
+        
+        // draw
+        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+            g.fill(path.getAwtShape());
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.draw(path.getAwtShape());
+        }
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+    
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix)
+     */
+    @Override
+    public void setMatrix(Matrix matrix) {
+        // since SetMatrix *replaces* all the other transformation, we have to restore/save
+        restore();
+        save();
+
+        // get the new current graphics
+        Graphics2D g = getGraphics2d();
+        
+        // and apply the matrix
+        g.setTransform(matrix.getTransform());
+        
+        if (mLogger != null && matrix.hasPerspective()) {
+            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
+        }
+    }
+
+    // --------------------
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipPath(android.graphics.Path, android.graphics.Region.Op)
+     */
+    @Override
+    public boolean clipPath(Path path, Op op) {
+        // TODO Auto-generated method stub
+        return super.clipPath(path, op);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipPath(android.graphics.Path)
+     */
+    @Override
+    public boolean clipPath(Path path) {
+        // TODO Auto-generated method stub
+        return super.clipPath(path);
+    }
+
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRegion(android.graphics.Region, android.graphics.Region.Op)
+     */
+    @Override
+    public boolean clipRegion(Region region, Op op) {
+        // TODO Auto-generated method stub
+        return super.clipRegion(region, op);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#clipRegion(android.graphics.Region)
+     */
+    @Override
+    public boolean clipRegion(Region region) {
+        // TODO Auto-generated method stub
+        return super.clipRegion(region);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
+     */
+    @Override
+    public void concat(Matrix matrix) {
+        // TODO Auto-generated method stub
+        super.concat(matrix);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
+     */
+    @Override
+    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+            Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint)
+     */
+    @Override
+    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
+            int vertOffset, int[] colors, int colorOffset, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.Rect)
+     */
+    @Override
+    public void drawPicture(Picture picture, Rect dst) {
+        // TODO Auto-generated method stub
+        super.drawPicture(picture, dst);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.RectF)
+     */
+    @Override
+    public void drawPicture(Picture picture, RectF dst) {
+        // TODO Auto-generated method stub
+        super.drawPicture(picture, dst);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture)
+     */
+    @Override
+    public void drawPicture(Picture picture) {
+        // TODO Auto-generated method stub
+        super.drawPicture(picture);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPoint(float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawPoint(float x, float y, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawPoint(x, y, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPoints(float[], int, int, android.graphics.Paint)
+     */
+    @Override
+    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawPoints(pts, offset, count, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPoints(float[], android.graphics.Paint)
+     */
+    @Override
+    public void drawPoints(float[] pts, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawPoints(pts, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPosText(char[], int, int, float[], android.graphics.Paint)
+     */
+    @Override
+    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawPosText(text, index, count, pos, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawPosText(java.lang.String, float[], android.graphics.Paint)
+     */
+    @Override
+    public void drawPosText(String text, float[] pos, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawPosText(text, pos, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawTextOnPath(char[] text, int index, int count, Path path, float offset,
+            float offset2, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawTextOnPath(text, index, count, path, offset, offset2, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint)
+     */
+    @Override
+    public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawTextOnPath(text, path, offset, offset2, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint)
+     */
+    @Override
+    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
+            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
+            int indexOffset, int indexCount, Paint paint) {
+        // TODO Auto-generated method stub
+        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset,
+                indices, indexOffset, indexCount, paint);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getDrawFilter()
+     */
+    @Override
+    public DrawFilter getDrawFilter() {
+        // TODO Auto-generated method stub
+        return super.getDrawFilter();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getGL()
+     */
+    @Override
+    public GL getGL() {
+        // TODO Auto-generated method stub
+        return super.getGL();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getMatrix()
+     */
+    @Override
+    public Matrix getMatrix() {
+        // TODO Auto-generated method stub
+        return super.getMatrix();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#getMatrix(android.graphics.Matrix)
+     */
+    @Override
+    public void getMatrix(Matrix ctm) {
+        // TODO Auto-generated method stub
+        super.getMatrix(ctm);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#isOpaque()
+     */
+    @Override
+    public boolean isOpaque() {
+        // TODO Auto-generated method stub
+        return super.isOpaque();
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#saveLayer(float, float, float, float, android.graphics.Paint, int)
+     */
+    @Override
+    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
+            int saveFlags) {
+        // TODO Auto-generated method stub
+        return super.saveLayer(left, top, right, bottom, paint, saveFlags);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint, int)
+     */
+    @Override
+    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
+        // TODO Auto-generated method stub
+        return super.saveLayer(bounds, paint, saveFlags);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int)
+     */
+    @Override
+    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
+            int saveFlags) {
+        // TODO Auto-generated method stub
+        return super.saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#saveLayerAlpha(android.graphics.RectF, int, int)
+     */
+    @Override
+    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
+        // TODO Auto-generated method stub
+        return super.saveLayerAlpha(bounds, alpha, saveFlags);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap)
+     */
+    @Override
+    public void setBitmap(Bitmap bitmap) {
+        // TODO Auto-generated method stub
+        super.setBitmap(bitmap);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter)
+     */
+    @Override
+    public void setDrawFilter(DrawFilter filter) {
+        // TODO Auto-generated method stub
+        super.setDrawFilter(filter);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#setViewport(int, int)
+     */
+    @Override
+    public void setViewport(int width, int height) {
+        // TODO Auto-generated method stub
+        super.setViewport(width, height);
+    }
+
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#skew(float, float)
+     */
+    @Override
+    public void skew(float sx, float sy) {
+        // TODO Auto-generated method stub
+        super.skew(sx, sy);
+    }
+
+
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
new file mode 100644
index 0000000..b426247
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+/**
+ * Constant definition class.<br>
+ * <br>
+ * Most constants have a prefix defining the content.
+ * <ul>
+ * <li><code>WS_</code> Workspace path constant. Those are absolute paths,
+ * from the project root.</li>
+ * <li><code>OS_</code> OS path constant. These paths are different depending on the platform.</li>
+ * <li><code>FN_</code> File name constant.</li>
+ * <li><code>FD_</code> Folder name constant.</li>
+ * <li><code>EXT_</code> File extension constant. This does NOT include a dot.</li>
+ * <li><code>DOT_</code> File extension constant. This start with a dot.</li>
+ * <li><code>RE_</code> Regexp constant.</li>
+ * <li><code>NS_</code> Namespace constant.</li>
+ * <li><code>CLASS_</code> Fully qualified class name.</li>
+ * </ul>
+ *
+ */
+public class BridgeConstants {
+
+    /** Namespace for the resource XML */
+    public final static String NS_RESOURCES = "http://schemas.android.com/apk/res/android";
+
+    public final static String R = "com.android.internal.R";
+
+    public final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
+    public final static String PREFIX_RESOURCE_REF = "@";
+    public final static String PREFIX_ANDROID_THEME_REF = "?android:";
+    public final static String PREFIX_THEME_REF = "?";
+    
+    public final static String PREFIX_ANDROID = "android:";
+    
+    public final static String RES_STYLE = "style";
+    public final static String RES_ATTR = "attr";
+    public final static String RES_DRAWABLE = "drawable";
+    public final static String RES_COLOR = "color";
+    public final static String RES_LAYOUT = "layout";
+    public final static String RES_STRING = "string";
+    public final static String RES_ID = "id";
+
+    public final static String REFERENCE_STYLE = RES_STYLE + "/";
+    public final static String REFERENCE_NULL = "@null";
+
+    public final static String FILL_PARENT = "fill_parent";
+    public final static String WRAP_CONTENT = "wrap_content";
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
new file mode 100644
index 0000000..727d6f2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.content.ContentResolver;
+import android.content.ContentServiceNative;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * A mock content resolver for the LayoutLib Bridge.
+ * <p/>
+ * It won't serve any actual data but it's good enough for all
+ * the widgets which expect to have a content resolver available via
+ * {@link BridgeContext#getContentResolver()}.
+ */
+public class BridgeContentResolver extends ContentResolver {
+
+    public BridgeContentResolver(Context context) {
+        super(context);
+    }
+
+    @Override
+    public IContentProvider acquireProvider(Context c, String name) {
+        // ignore
+        return null;
+    }
+
+    @Override
+    public boolean releaseProvider(IContentProvider icp) {
+        // ignore
+        return false;
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver observer) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void unregisterContentObserver(ContentObserver observer) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void startSync(Uri uri, Bundle extras) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void cancelSync(Uri uri) {
+        // pass
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
new file mode 100644
index 0000000..baa3d53
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -0,0 +1,1148 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.BridgeInflater;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+/**
+ * Custom implementation of Context to handle non compiled resources.
+ */
+public final class BridgeContext extends Context {
+    
+    private Resources mResources;
+    private Theme mTheme;
+    private HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
+    private IStyleResourceValue mThemeValues;
+    private final Object mProjectKey;
+    private Map<String, Map<String, IResourceValue>> mProjectResources;
+    private Map<String, Map<String, IResourceValue>> mFrameworkResources;
+    private Map<IStyleResourceValue, IStyleResourceValue> mStyleInheritanceMap;
+    
+    // maps for dynamically generated id representing style objects (IStyleResourceValue)
+    private Map<Integer, IStyleResourceValue> mDynamicIdToStyleMap;
+    private Map<IStyleResourceValue, Integer> mStyleToDynamicIdMap;
+    private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style
+    
+    // cache for TypedArray generated from IStyleResourceValue object
+    private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
+    private BridgeInflater mInflater;
+
+    private final IProjectCallback mProjectCallback;
+    private final ILayoutLog mLogger;
+    private BridgeContentResolver mContentResolver;
+
+    /**
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param metrics the {@link DisplayMetrics}.
+     * @param themeName The name of the theme to use.
+     * @param projectResources the resources of the project. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the
+     * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+     * and the value is the resource value.
+     * @param frameworkResources the framework resources. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the map
+     * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+     * value is the resource value.
+     * @param styleInheritanceMap
+     * @param customViewLoader
+     */
+    public BridgeContext(Object projectKey, DisplayMetrics metrics,
+            IStyleResourceValue currentTheme,
+            Map<String, Map<String, IResourceValue>> projectResources,
+            Map<String, Map<String, IResourceValue>> frameworkResources,
+            Map<IStyleResourceValue, IStyleResourceValue> styleInheritanceMap,
+            IProjectCallback customViewLoader, ILayoutLog logger) {
+        mProjectKey = projectKey;
+        mProjectCallback = customViewLoader;
+        mLogger = logger;
+        Configuration config = new Configuration();
+        
+        AssetManager assetManager = BridgeAssetManager.initSystem();
+        mResources = BridgeResources.initSystem(
+                this,
+                assetManager,
+                metrics,
+                config,
+                customViewLoader);
+        
+        mTheme = mResources.newTheme();
+        
+        mThemeValues = currentTheme;
+        mProjectResources = projectResources;
+        mFrameworkResources = frameworkResources;
+        mStyleInheritanceMap = styleInheritanceMap;
+    }
+    
+    public void setBridgeInflater(BridgeInflater inflater) {
+        mInflater = inflater;
+    }
+    
+    public void addViewKey(View view, Object viewKey) {
+        mViewKeyMap.put(view, viewKey);
+    }
+
+    public Object getViewKey(View view) {
+        return mViewKeyMap.get(view);
+    }
+    
+    public Object getProjectKey() {
+        return mProjectKey;
+    }
+    
+    public IProjectCallback getProjectCallback() {
+        return mProjectCallback;
+    }
+    
+    public ILayoutLog getLogger() {
+        return mLogger;
+    }
+    
+    // ------------ Context methods
+
+    @Override
+    public Resources getResources() {
+        return mResources;
+    }
+
+    @Override
+    public Theme getTheme() {
+        return mTheme;
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return this.getClass().getClassLoader();
+    }
+    
+    @Override
+    public Object getSystemService(String service) {
+        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
+            return mInflater;
+        }
+        
+        // AutoCompleteTextView and MultiAutoCompleteTextView want a window 
+        // service. We don't have any but it's not worth an exception.
+        if (WINDOW_SERVICE.equals(service)) {
+            return null;
+        }
+
+        throw new UnsupportedOperationException("Unsupported Service: " + service);
+    }
+
+
+    @Override
+    public final TypedArray obtainStyledAttributes(int[] attrs) {
+        return createStyleBasedTypedArray(mThemeValues, attrs);
+    }
+
+    @Override
+    public final TypedArray obtainStyledAttributes(int resid, int[] attrs)
+            throws Resources.NotFoundException {
+        // get the IStyleResourceValue based on the resId;
+        IStyleResourceValue style = getStyleByDynamicId(resid);
+        
+        if (style == null) {
+            throw new Resources.NotFoundException();
+        }
+
+        if (mTypedArrayCache == null) {
+            mTypedArrayCache = new HashMap<int[], Map<Integer,TypedArray>>();
+            
+            Map<Integer, TypedArray> map = new HashMap<Integer, TypedArray>();
+            mTypedArrayCache.put(attrs, map);
+
+            BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs);
+            map.put(resid, ta);
+            
+            return ta;
+        }
+        
+        // get the 2nd map
+        Map<Integer, TypedArray> map = mTypedArrayCache.get(attrs);
+        if (map == null) {
+            map = new HashMap<Integer, TypedArray>();
+            mTypedArrayCache.put(attrs, map);
+        }
+        
+        // get the array from the 2nd map
+        TypedArray ta = map.get(resid);
+        
+        if (ta == null) {
+            ta = createStyleBasedTypedArray(style, attrs);
+            map.put(resid, ta);
+        }
+        
+        return ta;
+    }
+
+    @Override
+    public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
+        return obtainStyledAttributes(set, attrs, 0, 0);
+    }
+    
+    @Override
+    public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
+            int defStyleAttr, int defStyleRes) {
+        
+        // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
+        BridgeXmlBlockParser parser = null;
+        if (set instanceof BridgeXmlBlockParser) {
+            parser = (BridgeXmlBlockParser)set;
+        } else {
+            // reall this should not be happening since its instantiated in Bridge
+            mLogger.error("Parser is not a BridgeXmlBlockParser!");
+            return null;
+        }
+
+        boolean[] frameworkAttributes = new boolean[1];
+        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes);
+        
+        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
+                parser.isPlatformFile());
+        
+        // resolve the defStyleAttr value into a IStyleResourceValue
+        IStyleResourceValue defStyleValues = null;
+        if (defStyleAttr != 0) {
+            // get the name from the int.
+            String defStyleName = searchAttr(defStyleAttr);
+
+            // look for the style in the current theme, and its parent:
+            if (mThemeValues != null) {
+                IResourceValue item = findItemInStyle(mThemeValues, defStyleName);
+                
+                if (item != null) {
+                    // item is a reference to a style entry. Search for it.
+                    item = findResValue(item.getValue());
+
+                    if (item instanceof IStyleResourceValue) {
+                        defStyleValues = (IStyleResourceValue)item;
+                    }
+                } else {
+                    // TODO: log the error properly
+                    System.out.println("Failed to find defStyle: " + defStyleName);
+                }
+            }
+        }
+        
+        if (defStyleRes != 0) {
+            // FIXME: See what we need to do with this.
+            throw new UnsupportedOperationException();
+        }
+        
+        String namespace = BridgeConstants.NS_RESOURCES;
+        if (frameworkAttributes[0] == false) {
+            // need to use the application namespace
+            namespace = mProjectCallback.getNamespace();
+        }
+
+        if (styleNameMap != null) {
+            for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
+                int index = styleAttribute.getKey().intValue();
+    
+                String name = styleAttribute.getValue();
+                String value = parser.getAttributeValue(namespace, name);
+                
+                // if there's no direct value for this attribute in the XML, we look for default
+                // values in the widget defStyle, and then in the theme.
+                if (value == null) {
+                    IResourceValue resValue = null;
+    
+                    // look for the value in the defStyle first (and its parent if needed)
+                    if (defStyleValues != null) {
+                        resValue = findItemInStyle(defStyleValues, name);
+                    }
+                    
+                    // if the item is not present in the defStyle, we look in the main theme (and
+                    // its parent themes)
+                    if (resValue == null && mThemeValues != null) {
+                        resValue = findItemInStyle(mThemeValues, name);
+                    }
+    
+                    // if we found a value, we make sure this doesn't reference another value.
+                    // So we resolve it.
+                    if (resValue != null) {
+                        resValue = resolveResValue(resValue);
+                    }
+                    
+                    ta.bridgeSetValue(index, name, resValue);
+                } else {
+                    // there is a value in the XML, but we need to resolve it in case it's
+                    // referencing another resource or a theme value.
+                    ta.bridgeSetValue(index, name, resolveValue(null, name, value));
+                }
+            }
+        }
+        
+        ta.sealArray();
+        
+        return ta;
+    }
+    
+    
+    // ------------- private new methods
+    
+    /**
+     * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
+     * values found in the given style.
+     * @see #obtainStyledAttributes(int, int[])
+     */
+    private BridgeTypedArray createStyleBasedTypedArray(IStyleResourceValue style, int[] attrs)
+            throws Resources.NotFoundException {
+        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, null);
+        
+        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
+                false /* platformResourceFlag */);
+        
+        // loop through all the values in the style map, and init the TypedArray with
+        // the style we got from the dynamic id
+        for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
+            int index = styleAttribute.getKey().intValue();
+
+            String name = styleAttribute.getValue();
+            
+            // get the value from the style, or its parent styles.
+            IResourceValue resValue = findItemInStyle(style, name);
+            
+            // resolve it to make sure there are no references left.
+            ta.bridgeSetValue(index, name, resolveResValue(resValue));
+        }
+        
+        ta.sealArray();
+
+        return ta;
+    }
+
+    
+    /**
+     * Resolves the value of a resource, if the value references a theme or resource value.
+     * <p/>
+     * This method ensures that it returns a {@link IResourceValue} object that does not
+     * reference another resource.
+     * If the resource cannot be resolved, it returns <code>null</code>.
+     * <p/>
+     * If a value that does not need to be resolved is given, the method will return a new
+     * instance of IResourceValue that contains the input value.
+     *
+     * @param type the type of the resource
+     * @param name the name of the attribute containing this value.
+     * @param value the resource value, or reference to resolve
+     * @return the resolved resource value or <code>null</code> if it failed to resolve it.
+     */
+    private IResourceValue resolveValue(String type, String name, String value) {
+        if (value == null) {
+            return null;
+        }
+
+        // get the IResourceValue referenced by this value
+        IResourceValue resValue = findResValue(value);
+        
+        // if resValue is null, but value is not null, this means it was not a reference.
+        // we return the name/value wrapper in a IResourceValue
+        if (resValue == null) {
+            return new ResourceValue(type, name, value);
+        }
+        
+        // we resolved a first reference, but we need to make sure this isn't a reference also.
+        return resolveResValue(resValue);
+    }
+
+    /**
+     * Returns the {@link IResourceValue} referenced by the value of <var>value</var>.
+     * <p/>
+     * This method ensures that it returns a {@link IResourceValue} object that does not
+     * reference another resource.
+     * If the resource cannot be resolved, it returns <code>null</code>.
+     * <p/>
+     * If a value that does not need to be resolved is given, the method will return the input
+     * value.
+     * 
+     * @param value the value containing the reference to resolve.
+     * @return a {@link IResourceValue} object or <code>null</code>
+     */
+    IResourceValue resolveResValue(IResourceValue value) {
+        if (value == null) {
+            return null;
+        }
+        
+        // if the resource value is a style, we simply return it.
+        if (value instanceof IStyleResourceValue) {
+            return value;
+        }
+
+        // else attempt to find another IResourceValue referenced by this one.
+        IResourceValue resolvedValue = findResValue(value.getValue());
+
+        // if the value did not reference anything, then we simply return the input value
+        if (resolvedValue == null) {
+            return value;
+        }
+
+        // otherwise, we attempt to resolve this new value as well
+        return resolveResValue(resolvedValue);
+    }
+    
+    /**
+     * Searches for, and returns a {@link IResourceValue} by its reference.
+     * <p/>
+     * The reference format can be:
+     * <pre>@resType/resName</pre>
+     * <pre>@android:resType/resName</pre>
+     * <pre>@resType/android:resName</pre>
+     * <pre>?resType/resName</pre>
+     * <pre>?android:resType/resName</pre>
+     * <pre>?resType/android:resName</pre>
+     * Any other string format will return <code>null</code>.
+     * <p/>
+     * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method
+     * only support the android namespace.
+     * 
+     * @param reference the resource reference to search for.
+     * @return a {@link IResourceValue} or <code>null</code>.
+     */
+    IResourceValue findResValue(String reference) {
+        if (reference == null) {
+            return null;
+        }
+        if (reference.startsWith(BridgeConstants.PREFIX_THEME_REF)) {
+            // no theme? no need to go further!
+            if (mThemeValues == null) {
+                return null;
+            }
+
+            boolean frameworkOnly = false;
+
+            // eleminate the prefix from the string
+            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_THEME_REF)) {
+                frameworkOnly = true;
+                reference = reference.substring(BridgeConstants.PREFIX_ANDROID_THEME_REF.length());
+            } else {
+                reference = reference.substring(BridgeConstants.PREFIX_THEME_REF.length());
+            }
+
+            // at this point, value can contain type/name (drawable/foo for instance).
+            // split it to make sure.
+            String[] segments = reference.split("\\/");
+
+            // we look for the referenced item name.
+            String referenceName = null;
+            
+            if (segments.length == 2) {
+                // there was a resType in the reference. If it's attr, we ignore it
+                // else, we assert for now.
+                if (BridgeConstants.RES_ATTR.equals(segments[0])) {
+                    referenceName = segments[1];
+                } else {
+                    // At this time, no support for ?type/name where type is not "attr"
+                    return null;
+                }
+            } else {
+                // it's just an item name.
+                referenceName = segments[0];
+            }
+            
+            // now we look for android: in the referenceName in order to support format
+            // such as: ?attr/android:name
+            if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) {
+                frameworkOnly = true;
+                referenceName = referenceName.substring(BridgeConstants.PREFIX_ANDROID.length());
+            }
+
+            // Now look for the item in the theme, starting with the current one.
+            if (frameworkOnly) {
+                // FIXME for now we do the same as if it didn't specify android:
+                return findItemInStyle(mThemeValues, referenceName);
+            }
+
+            return findItemInStyle(mThemeValues, referenceName);
+        } else if (reference.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
+            boolean frameworkOnly = false;
+            
+            // check for the specific null reference value.
+            if (BridgeConstants.REFERENCE_NULL.equals(reference)) { 
+                return null;
+            }
+
+            // Eliminate the prefix from the string.
+            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_RESOURCE_REF)) {
+                frameworkOnly = true;
+                reference = reference.substring(
+                        BridgeConstants.PREFIX_ANDROID_RESOURCE_REF.length());
+            } else {
+                reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
+            }
+            
+            // at this point, value contains type/[android:]name (drawable/foo for instance)
+            String[] segments = reference.split("\\/");
+            
+            // now we look for android: in the resource name in order to support format
+            // such as: @drawable/android:name
+            if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) {
+                frameworkOnly = true;
+                segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
+            }
+            
+            return findResValue(segments[0], segments[1], frameworkOnly);
+        }
+        
+        // Looks like the value didn't reference anything. Return null.
+        return null;
+    }
+
+    /**
+     * Searches for, and returns a {@link IResourceValue} by its name, and type.
+     * @param resType the type of the resource
+     * @param resName  the name of the resource
+     * @param frameworkOnly if <code>true</code>, the method does not search in the
+     * project resources
+     */
+    private IResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
+        // map of IResouceValue for the given type
+        Map<String, IResourceValue> typeMap;
+
+        // if allowed, search in the project resources first.
+        if (frameworkOnly == false) {
+            typeMap = mProjectResources.get(resType);
+            if (typeMap != null) {
+                IResourceValue item = typeMap.get(resName);
+                if (item != null) {
+                    return item;
+                }
+            }
+        }
+        
+        // now search in the framework resources.
+        typeMap = mFrameworkResources.get(resType);
+        if (typeMap != null) {
+            IResourceValue item = typeMap.get(resName);
+            if (item != null) {
+                return item;
+            }
+        }
+        
+        // didn't find the resource anywhere.
+        return null;
+    }
+    
+    /**
+     * Returns a framework resource by type and name. The returned resource is resolved.
+     * @param resourceType the type of the resource
+     * @param resourceName the name of the resource
+     */
+    public IResourceValue getFrameworkResource(String resourceType, String resourceName) {
+        return getResource(resourceType, resourceName, mFrameworkResources);
+    }
+    
+    /**
+     * Returns a project resource by type and name. The returned resource is resolved.
+     * @param resourceType the type of the resource
+     * @param resourceName the name of the resource
+     */
+    public IResourceValue getProjectResource(String resourceType, String resourceName) {
+        return getResource(resourceType, resourceName, mProjectResources);
+    }
+    
+    IResourceValue getResource(String resourceType, String resourceName,
+            Map<String, Map<String, IResourceValue>> resourceRepository) {
+        Map<String, IResourceValue> typeMap = resourceRepository.get(resourceType);
+        if (typeMap != null) {
+            IResourceValue item = typeMap.get(resourceName);
+            if (item != null) {
+                item = resolveResValue(item);
+                return item;
+            }
+        }
+        
+        // didn't find the resource anywhere.
+        return null;
+        
+    }
+    
+    /**
+     * Returns the {@link IResourceValue} matching a given name in a given style. If the
+     * item is not directly available in the style, the method looks in its parent style.
+     * @param style the style to search in
+     * @param itemName the name of the item to search for.
+     * @return the {@link IResourceValue} object or <code>null</code>
+     */
+    IResourceValue findItemInStyle(IStyleResourceValue style, String itemName) {
+        IResourceValue item = style.findItem(itemName);
+        
+        // if we didn't find it, we look in the parent style (if applicable)
+        if (item == null && mStyleInheritanceMap != null) {
+            IStyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
+            if (parentStyle != null) {
+                return findItemInStyle(parentStyle, itemName);
+            }
+        }
+        
+        return item;
+    }
+
+    /**
+     * The input int[] attrs is one of com.android.internal.R.styleable fields where the name
+     * of the field is the style being referenced and the array contains one index per attribute.
+     * <p/>
+     * searchAttrs() finds all the names of the attributes referenced so for example if
+     * attrs == com.android.internal.R.styleable.View, this returns the list of the "xyz" where
+     * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index
+     * that is used to reference the attribute later in the TypedArray.
+     * 
+     * @param attrs An attribute array reference given to obtainStyledAttributes.
+     * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the
+     *         attribute array. Returns null if nothing is found.
+     */
+    private TreeMap<Integer,String> searchAttrs(int[] attrs, boolean[] outFrameworkFlag) {
+        // get the name of the array from the framework resources
+        String arrayName = Bridge.resolveResourceValue(attrs);
+        if (arrayName != null) {
+            // if we found it, get the name of each of the int in the array.
+            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
+            for (int i = 0 ; i < attrs.length ; i++) {
+                String[] info = Bridge.resolveResourceValue(attrs[i]);
+                if (info != null) {
+                    attributes.put(i, info[0]);
+                } else {
+                    // FIXME Not sure what we should be doing here...
+                    attributes.put(i, null);
+                }
+            }
+            
+            if (outFrameworkFlag != null) {
+                outFrameworkFlag[0] = true;
+            }
+            
+            return attributes;
+        }
+        
+        // if the name was not found in the framework resources, look in the project
+        // resources
+        arrayName = mProjectCallback.resolveResourceValue(attrs);
+        if (arrayName != null) {
+            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
+            for (int i = 0 ; i < attrs.length ; i++) {
+                String[] info = mProjectCallback.resolveResourceValue(attrs[i]);
+                if (info != null) {
+                    attributes.put(i, info[0]);
+                } else {
+                    // FIXME Not sure what we should be doing here...
+                    attributes.put(i, null);
+                }
+            }
+
+            if (outFrameworkFlag != null) {
+                outFrameworkFlag[0] = false;
+            }
+
+            return attributes;
+        }
+
+        return null;
+    }
+
+    /**
+     * Searches for the attribute referenced by its internal id.
+     * 
+     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
+     * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null
+     *         if nothing is found.
+     */
+    public String searchAttr(int attr) {
+        String[] info = Bridge.resolveResourceValue(attr);
+        if (info != null) {
+            return info[0];
+        }
+        
+        info = mProjectCallback.resolveResourceValue(attr);
+        if (info != null) {
+            return info[0];
+        }
+        
+        return null;
+    }
+
+    int getDynamicIdByStyle(IStyleResourceValue resValue) {
+        if (mDynamicIdToStyleMap == null) {
+            // create the maps.
+            mDynamicIdToStyleMap = new HashMap<Integer, IStyleResourceValue>();
+            mStyleToDynamicIdMap = new HashMap<IStyleResourceValue, Integer>();
+        }
+        
+        // look for an existing id
+        Integer id = mStyleToDynamicIdMap.get(resValue);
+        
+        if (id == null) {
+            // generate a new id
+            id = Integer.valueOf(++mDynamicIdGenerator);
+            
+            // and add it to the maps.
+            mDynamicIdToStyleMap.put(id, resValue);
+            mStyleToDynamicIdMap.put(resValue, id);
+        }
+        
+        return id;
+    }
+    
+    private IStyleResourceValue getStyleByDynamicId(int i) {
+        if (mDynamicIdToStyleMap != null) {
+            return mDynamicIdToStyleMap.get(i);
+        }
+   
+        return null;
+    }
+
+    int getFrameworkIdValue(String idName, int defValue) {
+        Integer value = Bridge.getResourceValue(BridgeConstants.RES_ID, idName);
+        if (value != null) {
+            return value.intValue();
+        }
+        
+        return defValue;
+    }
+    
+    int getProjectIdValue(String idName, int defValue) {
+        if (mProjectCallback != null) {
+            Integer value = mProjectCallback.getResourceValue(BridgeConstants.RES_ID, idName);
+            if (value != null) {
+                return value.intValue();
+            }
+        }
+        
+        return defValue;
+    }
+
+    //------------ NOT OVERRIDEN --------------------
+
+    @Override
+    public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String arg0) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingPermission(String arg0) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkPermission(String arg0, int arg1, int arg2) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
+            int arg4, int arg5) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void clearWallpaper() {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public Context createPackageContext(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String[] databaseList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean deleteDatabase(String arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public boolean deleteFile(String arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String arg0, String arg1) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
+            String arg2) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforceCallingPermission(String arg0, String arg1) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
+            String arg4) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void enforceUriPermission(Uri arg0, String arg1, String arg2,
+            int arg3, int arg4, int arg5, String arg6) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public String[] fileList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getCacheDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        if (mContentResolver == null) {
+            mContentResolver = new BridgeContentResolver(this);
+        }
+        return mContentResolver;
+    }
+
+    @Override
+    public File getDatabasePath(String arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getDir(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getFileStreamPath(String arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getFilesDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getPackageName() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Drawable getWallpaper() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumWidth() {
+        return -1;
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumHeight() {
+        return -1;
+    }
+
+    @Override
+    public void grantUriPermission(String arg0, Uri arg1, int arg2) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public FileInputStream openFileInput(String arg0)
+            throws FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public FileOutputStream openFileOutput(String arg0, int arg1)
+            throws FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
+            CursorFactory arg2) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Drawable peekWallpaper() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
+            String arg2, Handler arg3) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void revokeUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void sendBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void sendBroadcast(Intent arg0, String arg1) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent arg0, String arg1) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent arg0, String arg1,
+            BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
+            Bundle arg6) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void setTheme(int arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void setWallpaper(Bitmap arg0) throws IOException {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void setWallpaper(InputStream arg0) throws IOException {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void startActivity(Intent arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName arg0, String arg1,
+            Bundle arg2) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public ComponentName startService(Intent arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean stopService(Intent arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public void unbindService(ServiceConnection arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver arg0) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public Context getApplicationContext() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
new file mode 100644
index 0000000..0bcc7fd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.InputStream;
+
+/**
+ * 
+ */
+public final class BridgeResources extends Resources {
+
+    private BridgeContext mContext;
+    private IProjectCallback mProjectCallback;
+    private boolean[] mPlatformResourceFlag = new boolean[1];
+    
+    /**
+     * This initializes the static field {@link Resources#mSystem} which is used
+     * by methods who get global resources using {@link Resources#getSystem()}.
+     * <p/>
+     * They will end up using our bridge resources.
+     * <p/>
+     * {@link Bridge} calls this method after setting up a new bridge.
+     */
+    /*package*/ static Resources initSystem(BridgeContext context, 
+            AssetManager assets,
+            DisplayMetrics metrics,
+            Configuration config,
+            IProjectCallback projectCallback) {
+        if (!(Resources.mSystem instanceof BridgeResources)) {
+            Resources.mSystem = new BridgeResources(context,
+                    assets,
+                    metrics,
+                    config,
+                    projectCallback);
+        }
+        return Resources.mSystem;
+    }
+    
+    /**
+     * Clears the static {@link Resources#mSystem} to make sure we don't leave objects
+     * around that would prevent us from unloading the library.
+     */
+    /*package*/ static void clearSystem() {
+        if (Resources.mSystem instanceof BridgeResources) {
+            ((BridgeResources)(Resources.mSystem)).mContext = null;
+            ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
+        }
+        Resources.mSystem = null;
+    }
+
+    private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
+            Configuration config, IProjectCallback projectCallback) {
+        super(assets, metrics, config);
+        mContext = context;
+        mProjectCallback = projectCallback;
+    }
+    
+    public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
+        return new BridgeTypedArray(this, mContext, numEntries, platformFile);
+    }
+    
+    private IResourceValue getResourceValue(int id, boolean[] platformResFlag_out) {
+        // first get the String related to this id in the framework
+        String[] resourceInfo = Bridge.resolveResourceValue(id);
+        
+        if (resourceInfo != null) {
+            platformResFlag_out[0] = true;
+            return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]);
+        }
+
+        // didn't find a match in the framework? look in the project.
+        if (mProjectCallback != null) {
+            resourceInfo = mProjectCallback.resolveResourceValue(id);
+            
+            if (resourceInfo != null) {
+                platformResFlag_out[0] = false;
+                return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]);
+            }
+        }
+
+        return null;
+    }
+    
+    @Override
+    public Drawable getDrawable(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            return ResourceHelper.getDrawable(value.getValue(), mContext, value.isFramework());
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+    
+    @Override
+    public int getColor(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            try {
+                return ResourceHelper.getColor(value.getValue());
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+    
+    @Override
+    public ColorStateList getColorStateList(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            try {
+                int color = ResourceHelper.getColor(value.getValue());
+                return ColorStateList.valueOf(color);
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+    
+    @Override
+    public CharSequence getText(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            return value.getValue();
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+    
+    @Override
+    public XmlResourceParser getLayout(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            File xml = new File(value.getValue());
+            if (xml.isFile()) {
+                // we need to create a pull parser around the layout XML file, and then
+                // give that to our XmlBlockParser
+                try {
+                    KXmlParser parser = new KXmlParser();
+                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                    parser.setInput(new FileReader(xml));
+                    
+                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                } catch (XmlPullParserException e) {
+                    mContext.getLogger().error(e);
+
+                    // we'll return null below.
+                } catch (FileNotFoundException e) {
+                    // this shouldn't happen since we check above.
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+    
+    @Override
+    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+        return mContext.obtainStyledAttributes(set, attrs);
+    }
+    
+    @Override
+    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+    
+    
+    @Override
+    public float getDimension(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (v.equals(BridgeConstants.FILL_PARENT)) {
+                    return LayoutParams.FILL_PARENT;
+                } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
+                    return LayoutParams.WRAP_CONTENT;
+                }
+            
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return mTmpValue.getDimension(mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getDimensionPixelOffset(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return TypedValue.complexToDimensionPixelOffset(mTmpValue.data, mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getDimensionPixelSize(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return TypedValue.complexToDimensionPixelSize(mTmpValue.data, mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getInteger(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null && value.getValue() != null) {
+            String v = value.getValue();
+            int radix = 10;
+            if (v.startsWith("0x")) {
+                v = v.substring(2);
+                radix = 16;
+            }
+            try {
+                return Integer.parseInt(v, radix);
+            } catch (NumberFormatException e) {
+                // return exception below
+            }
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public String getResourceEntryName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getResourceName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getResourceTypeName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getString(int id, Object... formatArgs) throws NotFoundException {
+        String s = getString(id);
+        if (s != null) {
+            return String.format(s, formatArgs);
+            
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Override
+    public String getString(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null && value.getValue() != null) {
+            return value.getValue();
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Override
+    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, outValue)) {
+                    return;
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+    }
+
+    @Override
+    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public XmlResourceParser getXml(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                // check this is a file
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        KXmlParser parser = new KXmlParser();
+                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                        parser.setInput(new FileReader(f));
+                        
+                        return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    } catch (XmlPullParserException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    } catch (FileNotFoundException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Override
+    public InputStream openRawResource(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                // check this is a file
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        return new FileInputStream(f);
+                    } catch (FileNotFoundException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    }
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Override
+    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
+     * @param id the id of the resource
+     * @throws NotFoundException
+     */
+    private void throwException(int id) throws NotFoundException {
+        // first get the String related to this id in the framework
+        String[] resourceInfo = Bridge.resolveResourceValue(id);
+
+        // if the name is unknown in the framework, get it from the custom view loader.
+        if (resourceInfo == null && mProjectCallback != null) {
+            resourceInfo = mProjectCallback.resolveResourceValue(id);
+        }
+        
+        String message = null;
+        if (resourceInfo != null) {
+            message = String.format(
+                    "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
+                    resourceInfo[1], id, resourceInfo[0]);
+        } else {
+            message = String.format(
+                    "Could not resolve resource value: 0x%1$X.", id);
+        }
+        
+        throw new NotFoundException(message);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
new file mode 100644
index 0000000..f5da91d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
@@ -0,0 +1,781 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.internal.util.XmlUtils;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Map;
+
+/**
+ * TODO: describe.
+ */
+public final class BridgeTypedArray extends TypedArray {
+    
+    @SuppressWarnings("hiding")
+    private BridgeResources mResources;
+    private BridgeContext mContext;
+    @SuppressWarnings("hiding")
+    private IResourceValue[] mData;
+    private String[] mNames;
+    private final boolean mPlatformFile;
+    
+    public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+            boolean platformFile) {
+        super(null, null, null, 0);
+        mResources = resources;
+        mContext = context;
+        mPlatformFile = platformFile;
+        mData = new IResourceValue[len];
+        mNames = new String[len];
+    }
+
+    /** A bridge-specific method that sets a value in the type array */ 
+    public void bridgeSetValue(int index, String name, IResourceValue value) {
+        mData[index] = value;
+        mNames[index] = name;
+    }
+    
+    /**
+     * Seals the array after all calls to {@link #bridgeSetValue(int, String, IResourceValue)} have
+     * been done.
+     * <p/>This allows to compute the list of non default values, permitting
+     * {@link #getIndexCount()} to return the proper value.
+     */
+    public void sealArray() {
+        // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
+        // first count the array size
+        int count = 0;
+        for (IResourceValue data : mData) {
+            if (data != null) {
+                count++;
+            }
+        }
+        
+        // allocate the table with an extra to store the size
+        mIndices = new int[count+1];
+        mIndices[0] = count;
+        
+        // fill the array with the indices.
+        int index = 1;
+        for (int i = 0 ; i < mData.length ; i++) {
+            if (mData[i] != null) {
+                mIndices[index++] = i;
+            }
+        }
+    }
+
+    /**
+     * Return the number of values in this array.
+     */
+    @Override
+    public int length() {
+        return mData.length;
+    }
+    
+    /**
+     * Return the Resources object this array was loaded from.
+     */
+    @Override
+    public Resources getResources() {
+        return mResources;
+    }
+    
+    /**
+     * Retrieve the styled string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence holding string data.  May be styled.  Returns 
+     *         null if the attribute is not defined.
+     */
+    @Override
+    public CharSequence getText(int index) {
+        if (mData[index] != null) {
+            // FIXME: handle styled strings!
+            return mData[index].getValue();
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieve the string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return String holding string data.  Any styling information is
+     * removed.  Returns null if the attribute is not defined.
+     */
+    @Override
+    public String getString(int index) {
+        if (mData[index] != null) {
+            return mData[index].getValue();
+        }
+        
+        return null;
+    }
+
+    /**
+     * Retrieve the boolean value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute boolean value, or defValue if not defined.
+     */
+    @Override
+    public boolean getBoolean(int index, boolean defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+        if (s != null) {
+            return XmlUtils.convertValueToBoolean(s, defValue);
+        }
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute int value, or defValue if not defined.
+     */
+    @Override
+    public int getInt(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+        
+        try {
+            return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
+        } catch (NumberFormatException e) {
+            // pass
+        }
+
+        // Field is not null and is not an integer.
+        // Check for possible constants and try to find them.
+        // Get the map of attribute-constant -> IntegerValue
+        Map<String, Integer> map = Bridge.getEnumValues(mNames[index]);
+        
+        if (map != null) {
+            // accumulator to store the value of the 1+ constants.
+            int result = 0;
+            
+            // split the value in case this is a mix of several flags.
+            String[] keywords = s.split("\\|");
+            for (String keyword : keywords) {
+                Integer i = map.get(keyword.trim());
+                if (i != null) {
+                    result |= i.intValue();
+                } else {
+                    mContext.getLogger().warning(String.format(
+                            "Unknown constant \"%s\" in attribute \"%2$s\"",
+                            keyword, mNames[index]));
+                }
+            }
+            return result;
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve the float value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Attribute float value, or defValue if not defined..
+     */
+    @Override
+    public float getFloat(int index, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s != null) {
+            try {
+                return Float.parseFloat(s);
+            } catch (NumberFormatException e) {
+                mContext.getLogger().warning(String.format(
+                        "Unable to convert \"%s\" into a float in attribute \"%2$s\"",
+                        s, mNames[index]));
+                
+                // we'll return the default value below.
+            }
+        }
+        return defValue;
+    }
+    
+    /**
+     * Retrieve the color value for the attribute at <var>index</var>.  If
+     * the attribute references a color resource holding a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute color value, or defValue if not defined.
+     */
+    @Override
+    public int getColor(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+        
+        String s = mData[index].getValue();
+        try {
+            return ResourceHelper.getColor(s);
+        } catch (NumberFormatException e) {
+            mContext.getLogger().warning(String.format(
+                    "Unable to convert \"%s\" into a color in attribute \"%2$s\"",
+                    s, mNames[index]));
+
+            // we'll return the default value below.
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve the ColorStateList for the attribute at <var>index</var>.
+     * The value may be either a single solid color or a reference to
+     * a color or complex {@link android.content.res.ColorStateList} description.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return ColorStateList for the attribute, or null if not defined.
+     */
+    @Override
+    public ColorStateList getColorStateList(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+
+        if (value == null) {
+            return null;
+        }
+        
+        try {
+            int color = ResourceHelper.getColor(value);
+            return ColorStateList.valueOf(color);
+        } catch (NumberFormatException e) {
+            // if it's not a color value, we'll attempt to read the xml based color below.
+        }
+        
+        // let the framework inflate the ColorStateList from the XML file.
+        try {
+            File f = new File(value);
+            if (f.isFile()) {
+                KXmlParser parser = new KXmlParser();
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                parser.setInput(new FileReader(f));
+                
+                ColorStateList colorStateList = ColorStateList.createFromXml(
+                        mContext.getResources(),
+                        // FIXME: we need to know if this resource is platform or not
+                        new BridgeXmlBlockParser(parser, mContext, false));
+                return colorStateList;
+            }
+        } catch (Exception e) {
+            // this is an error and not warning since the file existence is checked before
+            // attempting to parse it.
+            mContext.getLogger().error(e);
+
+            // return null below.
+        }
+        
+        // looks like were unable to resolve the color value.
+        mContext.getLogger().warning(String.format(
+                "Unable to resolve color value \"%1$s\" in attribute \"%2$s\"",
+                value, mNames[index]));
+        
+        return null;
+    }
+    
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute integer value, or defValue if not defined.
+     */
+    @Override
+    public int getInteger(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s != null) {
+            try {
+                return Integer.parseInt(s);
+            } catch (NumberFormatException e) {
+                mContext.getLogger().warning(String.format(
+                        "Unable to convert \"%s\" into a integer in attribute \"%2$s\"",
+                        s, mNames[index]));
+
+                // The default value is returned below.
+            }
+        }
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit 
+     * conversions are based on the current {@link DisplayMetrics} 
+     * associated with the resources this {@link TypedArray} object 
+     * came from. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric, or defValue if not defined.
+     * 
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public float getDimension(int index, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s == null) {
+            return defValue;
+        } else if (s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.FILL_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        }
+        
+        if (ResourceHelper.stringToFloat(s, mValue)) {
+            return mValue.getDimension(mResources.mMetrics);
+        }
+        
+        // looks like we were unable to resolve the dimension value
+        mContext.getLogger().warning(String.format(
+                "Unable to resolve dimension value \"%1$s\" in attribute \"%2$s\"",
+                s, mNames[index]));
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     * 
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public int getDimensionPixelOffset(int index, int defValue) {
+        return (int) getDimension(index, defValue);
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     *  
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    @Override
+    public int getDimensionPixelSize(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s == null) {
+            return defValue;
+        } else if (s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.FILL_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        }
+        
+        // FIXME huh?
+
+        float f = getDimension(index, defValue);
+        final int res = (int)(f+0.5f);
+        if (res != 0) return res;
+        if (f == 0) return 0;
+        if (f > 0) return 1;
+
+        throw new UnsupportedOperationException("Can't convert to dimension: " +
+                Integer.toString(index));
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     * 
+     * @param index Index of the attribute to retrieve.
+     * @param name Textual name of attribute for error reporting.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     */
+    @Override
+    public int getLayoutDimension(int index, String name) {
+        return getDimensionPixelSize(index, 0);
+    }
+
+    /**
+     * Retrieve a fractional unit attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve. 
+     * @param base The base value of this fraction.  In other words, a 
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other 
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute fractional value multiplied by the appropriate 
+     * base value, or defValue if not defined. 
+     */
+    @Override
+    public float getFraction(int index, int base, int pbase, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null) {
+            return defValue;
+        }
+        
+        if (ResourceHelper.stringToFloat(value, mValue)) {
+            return mValue.getFraction(base, pbase);
+        }
+
+        // looks like we were unable to resolve the fraction value
+        mContext.getLogger().warning(String.format(
+                "Unable to resolve fraction value \"%1$s\" in attribute \"%2$s\"",
+                value, mNames[index]));
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve the resource identifier for the attribute at
+     * <var>index</var>.  Note that attribute resource as resolved when 
+     * the overall {@link TypedArray} object is retrieved.  As a 
+     * result, this function will return the resource identifier of the 
+     * final resource value that was found, <em>not</em> necessarily the 
+     * original resource that was specified by the attribute. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute resource identifier, or defValue if not defined.
+     */
+    @Override
+    public int getResourceId(int index, int defValue) {
+        // get the IResource for this index
+        IResourceValue resValue = mData[index];
+        
+        // no data, return the default value.
+        if (resValue == null) {
+            return defValue;
+        }
+
+        // check if this is a style resource
+        if (resValue instanceof IStyleResourceValue) {
+            // get the id that will represent this style.
+            return mContext.getDynamicIdByStyle((IStyleResourceValue)resValue);
+        }
+        
+        // if the attribute was a reference to an id, and not a declaration of an id (@+id), then
+        // the xml attribute value was "resolved" which leads us to a IResourceValue with
+        // getType() returning "id" and getName() returning the id name
+        // (and getValue() returning null!). We need to handle this!
+        if (resValue.getType() != null && resValue.getType().equals(BridgeConstants.RES_ID)) {
+            // if this is a framework id
+            if (mPlatformFile || resValue.isFramework()) {
+                // look for idName in the android R classes
+                return mContext.getFrameworkIdValue(resValue.getName(), defValue);
+            }
+
+            // look for idName in the project R class.
+            return mContext.getProjectIdValue(resValue.getName(), defValue);
+        }
+
+        // else, try to get the value, and resolve it somehow.
+        String value = resValue.getValue();
+        if (value == null) {
+            return defValue;
+        }
+        
+        // if the value is just an integer, return it.
+        try {
+            int i = Integer.parseInt(value);
+            if (Integer.toString(i).equals(value)) {
+                return i;
+            }
+        } catch (NumberFormatException e) {
+            // pass
+        }
+
+        // Handle the @id/<name>, @+id/<name> and @android:id/<name>
+        // We need to return the exact value that was compiled (from the various R classes),
+        // as these values can be reused internally with calls to findViewById().
+        // There's a trick with platform layouts that not use "android:" but their IDs are in
+        // fact in the android.R and com.android.internal.R classes.
+        // The field mPlatformFile will indicate that all IDs are to be looked up in the android R
+        // classes exclusively.
+        
+        // if this is a reference to an id, find it.
+        if (value.startsWith("@id/") || value.startsWith("@+") ||
+                value.startsWith("@android:id/")) {
+            
+            int pos = value.indexOf('/');
+            String idName = value.substring(pos + 1);
+            
+            // if this is a framework id
+            if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
+                // look for idName in the android R classes
+                return mContext.getFrameworkIdValue(idName, defValue);
+            }
+
+            // look for idName in the project R class.
+            return mContext.getProjectIdValue(idName, defValue);
+        }
+
+        // not a direct id valid reference? resolve it
+        Integer idValue = null;
+        
+        if (resValue.isFramework()) {
+            idValue = Bridge.getResourceValue(resValue.getType(), resValue.getName());
+        } else {
+            idValue = mContext.getProjectCallback().getResourceValue(
+                    resValue.getType(), resValue.getName());
+        }
+
+        if (idValue != null) {
+            return idValue.intValue();
+        }
+        
+        mContext.getLogger().warning(String.format(
+                "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]));
+        return defValue;
+    }
+
+    /**
+     * Retrieve the Drawable for the attribute at <var>index</var>.  This
+     * gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getDrawable Resources.getDrawable} of the owning
+     * Resources object to retrieve its Drawable.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Drawable for the attribute, or null if not defined.
+     */
+    @Override
+    public Drawable getDrawable(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null || BridgeConstants.REFERENCE_NULL.equals(value)) {
+            return null;
+        }
+        
+        Drawable d = ResourceHelper.getDrawable(value, mContext, mData[index].isFramework());
+        
+        if (d != null) {
+            return d;
+        }
+        
+        // looks like we were unable to resolve the drawable
+        mContext.getLogger().warning(String.format(
+                "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", value, mNames[index]));
+
+        return null;
+    }
+
+
+    /**
+     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+     * This gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getTextArray Resources.getTextArray} of the owning
+     * Resources object to retrieve its String[].
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence[] for the attribute, or null if not defined.
+     */
+    @Override
+    public CharSequence[] getTextArray(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null) {
+            return null;
+        }
+        
+        throw new UnsupportedOperationException(
+                String.format("BridgeTypedArray: UNKNOWN VALUE FOR getTextArray(%d) => %s", //DEBUG
+                index, value));
+
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param outValue TypedValue object in which to place the attribute's
+     *                 data.
+     * 
+     * @return Returns true if the value was retrieved, else false. 
+     */
+    @Override
+    public boolean getValue(int index, TypedValue outValue) {
+        if (mData[index] == null) {
+            return false;
+        }
+        
+        String s = mData[index].getValue();
+        
+        return ResourceHelper.stringToFloat(s, outValue);
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return True if the attribute has a value, false otherwise.
+     */
+    @Override
+    public boolean hasValue(int index) {
+        return mData[index] != null;
+    }
+    
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var> 
+     * and return a temporary object holding its data.  This object is only 
+     * valid until the next call on to {@link TypedArray}. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Returns a TypedValue object if the attribute is defined, 
+     *         containing its data; otherwise returns null.  (You will not
+     *         receive a TypedValue whose type is TYPE_NULL.)
+     */
+    @Override
+    public TypedValue peekValue(int index) {
+        if (getValue(index, mValue)) {
+            return mValue;
+        }
+        
+        return null;
+    }
+
+    /**
+     * Returns a message about the parser state suitable for printing error messages.
+     */
+    @Override
+    public String getPositionDescription() {
+        return "<internal -- stub if needed>";
+    }
+
+    /**
+     * Give back a previously retrieved StyledAttributes, for later re-use.
+     */
+    @Override
+    public void recycle() {
+        // pass
+    }
+
+    @Override
+    public boolean getValueAt(int index, TypedValue outValue) {
+        // pass
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return mData.toString();
+    }
+ }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
new file mode 100644
index 0000000..d842a66
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.IXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.XmlPullAttributes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * {@link BridgeXmlBlockParser} reimplements most of android.xml.XmlBlock.Parser.
+ * It delegates to both an instance of {@link XmlPullParser} and an instance of
+ * {@link XmlPullAttributes} (for the {@link AttributeSet} part).
+ */
+public class BridgeXmlBlockParser implements XmlResourceParser {
+
+    private XmlPullParser mParser;
+    private XmlPullAttributes mAttrib;
+
+    private boolean mStarted = false;
+    private boolean mDecNextDepth = false;
+    private int mDepth = 0;
+    private int mEventType = START_DOCUMENT;
+    private final boolean mPlatformFile;
+
+    /**
+     * Builds a {@link BridgeXmlBlockParser}.
+     * @param parser The XmlPullParser to get the content from.
+     * @param context the Context.
+     * @param platformFile Indicates whether the the file is a platform file or not.
+     */
+    public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
+        mParser = parser;
+        mPlatformFile = platformFile;
+        mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+    }
+    
+    public boolean isPlatformFile() {
+        return mPlatformFile;
+    }
+
+    public Object getViewKey() {
+        if (mParser instanceof IXmlPullParser) {
+            return ((IXmlPullParser)mParser).getViewKey();
+        }
+
+        return null;
+    }
+    
+    
+    // ------- XmlResourceParser implementation
+    
+    public void setFeature(String name, boolean state)
+            throws XmlPullParserException {
+        if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
+            return;
+        }
+        if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
+            return;
+        }
+        throw new XmlPullParserException("Unsupported feature: " + name);
+    }
+
+    public boolean getFeature(String name) {
+        if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
+            return true;
+        }
+        if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
+            return true;
+        }
+        return false;
+    }
+
+    public void setProperty(String name, Object value) throws XmlPullParserException {
+        throw new XmlPullParserException("setProperty() not supported");
+    }
+
+    public Object getProperty(String name) {
+        return null;
+    }
+
+    public void setInput(Reader in) throws XmlPullParserException {
+        mParser.setInput(in);
+    }
+
+    public void setInput(InputStream inputStream, String inputEncoding)
+            throws XmlPullParserException {
+        mParser.setInput(inputStream, inputEncoding);
+    }
+
+    public void defineEntityReplacementText(String entityName,
+            String replacementText) throws XmlPullParserException {
+        throw new XmlPullParserException(
+                "defineEntityReplacementText() not supported");
+    }
+
+    public String getNamespacePrefix(int pos) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespacePrefix() not supported");
+    }
+
+    public String getInputEncoding() {
+        return null;
+    }
+
+    public String getNamespace(String prefix) {
+        throw new RuntimeException("getNamespace() not supported");
+    }
+
+    public int getNamespaceCount(int depth) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespaceCount() not supported");
+    }
+
+    public String getPositionDescription() {
+        return "Binary XML file line #" + getLineNumber();
+    }
+
+    public String getNamespaceUri(int pos) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespaceUri() not supported");
+    }
+
+    public int getColumnNumber() {
+        return -1;
+    }
+
+    public int getDepth() {
+        return mDepth;
+    }
+
+    public String getText() {
+        return mParser.getText();
+    }
+
+    public int getLineNumber() {
+        return mParser.getLineNumber();
+    }
+
+    public int getEventType() {
+        return mEventType;
+    }
+
+    public boolean isWhitespace() throws XmlPullParserException {
+        // Original comment: whitespace was stripped by aapt.
+        return mParser.isWhitespace();
+    }
+
+    public String getPrefix() {
+        throw new RuntimeException("getPrefix not supported");
+    }
+
+    public char[] getTextCharacters(int[] holderForStartAndLength) {
+        String txt = getText();
+        char[] chars = null;
+        if (txt != null) {
+            holderForStartAndLength[0] = 0;
+            holderForStartAndLength[1] = txt.length();
+            chars = new char[txt.length()];
+            txt.getChars(0, txt.length(), chars, 0);
+        }
+        return chars;
+    }
+
+    public String getNamespace() {
+        return mParser.getNamespace();
+    }
+
+    public String getName() {
+        return mParser.getName();
+    }
+
+    public String getAttributeNamespace(int index) {
+        return mParser.getAttributeNamespace(index);
+    }
+
+    public String getAttributeName(int index) {
+        return mParser.getAttributeName(index);
+    }
+
+    public String getAttributePrefix(int index) {
+        throw new RuntimeException("getAttributePrefix not supported");
+    }
+
+    public boolean isEmptyElementTag() {
+        // XXX Need to detect this.
+        return false;
+    }
+
+    public int getAttributeCount() {
+        return mParser.getAttributeCount();
+    }
+
+    public String getAttributeValue(int index) {
+        return mParser.getAttributeValue(index);
+    }
+
+    public String getAttributeType(int index) {
+        return "CDATA";
+    }
+
+    public boolean isAttributeDefault(int index) {
+        return false;
+    }
+
+    public int nextToken() throws XmlPullParserException, IOException {
+        return next();
+    }
+
+    public String getAttributeValue(String namespace, String name) {
+        return mParser.getAttributeValue(namespace, name);
+    }
+
+    public int next() throws XmlPullParserException, IOException {
+        if (!mStarted) {
+            mStarted = true;
+            return START_DOCUMENT;
+        }
+        int ev = mParser.next();
+        if (mDecNextDepth) {
+            mDepth--;
+            mDecNextDepth = false;
+        }
+        switch (ev) {
+        case START_TAG:
+            mDepth++;
+            break;
+        case END_TAG:
+            mDecNextDepth = true;
+            break;
+        }
+        mEventType = ev;
+        return ev;
+    }
+
+    public void require(int type, String namespace, String name)
+            throws XmlPullParserException {
+        if (type != getEventType()
+                || (namespace != null && !namespace.equals(getNamespace()))
+                || (name != null && !name.equals(getName())))
+            throw new XmlPullParserException("expected " + TYPES[type]
+                    + getPositionDescription());
+    }
+
+    public String nextText() throws XmlPullParserException, IOException {
+        if (getEventType() != START_TAG) {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": parser must be on START_TAG to read next text", this,
+                    null);
+        }
+        int eventType = next();
+        if (eventType == TEXT) {
+            String result = getText();
+            eventType = next();
+            if (eventType != END_TAG) {
+                throw new XmlPullParserException(
+                        getPositionDescription()
+                                + ": event TEXT it must be immediately followed by END_TAG",
+                        this, null);
+            }
+            return result;
+        } else if (eventType == END_TAG) {
+            return "";
+        } else {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": parser must be on START_TAG or TEXT to read text",
+                    this, null);
+        }
+    }
+
+    public int nextTag() throws XmlPullParserException, IOException {
+        int eventType = next();
+        if (eventType == TEXT && isWhitespace()) { // skip whitespace
+            eventType = next();
+        }
+        if (eventType != START_TAG && eventType != END_TAG) {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": expected start or end tag", this, null);
+        }
+        return eventType;
+    }
+
+    // AttributeSet implementation
+
+    
+    public void close() {
+        // pass
+    }
+
+    public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+        return mAttrib.getAttributeBooleanValue(index, defaultValue);
+    }
+
+    public boolean getAttributeBooleanValue(String namespace, String attribute,
+            boolean defaultValue) {
+        return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
+    }
+
+    public float getAttributeFloatValue(int index, float defaultValue) {
+        return mAttrib.getAttributeFloatValue(index, defaultValue);
+    }
+
+    public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
+        return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeIntValue(int index, int defaultValue) {
+        return mAttrib.getAttributeIntValue(index, defaultValue);
+    }
+
+    public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeListValue(int index, String[] options, int defaultValue) {
+        return mAttrib.getAttributeListValue(index, options, defaultValue);
+    }
+
+    public int getAttributeListValue(String namespace, String attribute,
+            String[] options, int defaultValue) {
+        return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
+    }
+
+    public int getAttributeNameResource(int index) {
+        return mAttrib.getAttributeNameResource(index);
+    }
+
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        return mAttrib.getAttributeResourceValue(index, defaultValue);
+    }
+
+    public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+        return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
+    }
+
+    public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
+    }
+
+    public String getClassAttribute() {
+        return mAttrib.getClassAttribute();
+    }
+
+    public String getIdAttribute() {
+        return mAttrib.getIdAttribute();
+    }
+
+    public int getIdAttributeResourceValue(int defaultValue) {
+        return mAttrib.getIdAttributeResourceValue(defaultValue);
+    }
+
+    public int getStyleAttribute() {
+        return mAttrib.getStyleAttribute();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
new file mode 100644
index 0000000..4be6eab
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.IResourceValue;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.util.AttributeSet;
+import android.util.XmlPullAttributes;
+
+/**
+ * A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
+ */
+public class BridgeXmlPullAttributes extends XmlPullAttributes {
+
+    private final BridgeContext mContext;
+    private final boolean mPlatformFile;
+
+    public BridgeXmlPullAttributes(XmlPullParser parser, BridgeContext context,
+            boolean platformFile) {
+        super(parser);
+        mContext = context;
+        mPlatformFile = platformFile;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeNameResource(int)
+     * 
+     * This methods must return com.android.internal.R.attr.<name> matching
+     * the name of the attribute.
+     * It returns 0 if it doesn't find anything.
+     */
+    @Override
+    public int getAttributeNameResource(int index) {
+        // get the attribute name.
+        String name = getAttributeName(index);
+        
+        // get the attribute namespace
+        String ns = mParser.getAttributeNamespace(index);
+        
+        if (BridgeConstants.NS_RESOURCES.equals(ns)) {
+            Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
+            if (v != null) {
+                return v.intValue();
+            }
+            
+            return 0;
+        }
+        
+        // this is not an attribute in the android namespace, we query the customviewloader, if
+        // the namespaces match.
+        if (mContext.getProjectCallback().getNamespace().equals(ns)) {
+            Integer v = mContext.getProjectCallback().getResourceValue(BridgeConstants.RES_ATTR,
+                    name);
+            if (v != null) {
+                return v.intValue();
+            }
+        }
+
+        return 0;
+    }
+    
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeResourceValue(int, int)
+     */
+    @Override
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        String value = getAttributeValue(index);
+        
+        return resolveResourceValue(value, defaultValue);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeResourceValue(java.lang.String, java.lang.String, int)
+     */
+    @Override
+    public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        
+        return resolveResourceValue(value, defaultValue);
+    }
+
+    private int resolveResourceValue(String value, int defaultValue) {
+        // now look for this particular value
+        IResourceValue resource = mContext.resolveResValue(mContext.findResValue(value));
+        
+        if (resource != null) {
+            Integer id = null;
+            if (mPlatformFile || resource.isFramework()) {
+                id = Bridge.getResourceValue(resource.getType(), resource.getName());
+            } else {
+                id = mContext.getProjectCallback().getResourceValue(
+                        resource.getType(), resource.getName());
+            }
+
+            if (id != null) {
+                return id;
+            }
+        }
+        
+        return defaultValue;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
new file mode 100644
index 0000000..1bdd1cc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.graphics.Typeface;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Provides {@link Font} object to the layout lib.
+ * <p/>
+ * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
+ * fonts.xml file located alongside the ttf files.
+ */
+public final class FontLoader {
+    private static final String FONTS_DEFINITIONS = "fonts.xml";
+    
+    private static final String NODE_FONTS = "fonts";
+    private static final String NODE_FONT = "font";
+    private static final String NODE_NAME = "name";
+    
+    private static final String ATTR_TTF = "ttf";
+
+    private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME };
+
+    private static final String FONT_EXT = ".ttf";
+
+    private static final String[] FONT_STYLE_DEFAULT = { "", "-Regular" };
+    private static final String[] FONT_STYLE_BOLD = { "-Bold" };
+    private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
+    private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
+    
+    // list of font style, in the order matching the Typeface Font style
+    private static final String[][] FONT_STYLES = {
+        FONT_STYLE_DEFAULT,
+        FONT_STYLE_BOLD,
+        FONT_STYLE_ITALIC,
+        FONT_STYLE_BOLDITALIC
+    };
+    
+    private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
+    private final Map<String, Map<Integer, Font>> mTtfToFontMap =
+        new HashMap<String, Map<Integer, Font>>();
+    
+    public static FontLoader create(String fontOsLocation) {
+        try {
+            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+                parserFactory.setNamespaceAware(true);
+    
+            SAXParser parser = parserFactory.newSAXParser();
+            File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
+            
+            FontDefinitionParser definitionParser = new FontDefinitionParser(
+                    fontOsLocation + File.separator);
+            parser.parse(new FileInputStream(f), definitionParser);
+            
+            return definitionParser.getFontLoader();
+        } catch (ParserConfigurationException e) {
+            // return null below
+        } catch (SAXException e) {
+            // return null below
+        } catch (FileNotFoundException e) {
+            // return null below
+        } catch (IOException e) {
+            // return null below
+        }
+
+        return null;
+    }
+
+    private FontLoader(List<FontInfo> fontList) {
+        for (FontInfo info : fontList) {
+            for (String family : info.families) {
+                mFamilyToTtf.put(family, info.ttf);
+            }
+        }
+    }
+
+    public synchronized Font getFont(String family, int[] style) {
+        if (family == null) {
+            return null;
+        }
+
+        // get the ttf name from the family
+        String ttf = mFamilyToTtf.get(family);
+        
+        if (ttf == null) {
+            return null;
+        }
+        
+        // get the font from the ttf
+        Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
+        
+        if (styleMap == null) {
+            styleMap = new HashMap<Integer, Font>();
+            mTtfToFontMap.put(ttf, styleMap);
+        }
+        
+        Font f = styleMap.get(style);
+        
+        if (f != null) {
+            return f;
+        }
+        
+        // if it doesn't exist, we create it, and we can't, we try with a simpler style
+        switch (style[0]) {
+            case Typeface.NORMAL:
+                f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                break;
+            case Typeface.BOLD:
+            case Typeface.ITALIC:
+                f = getFont(ttf, FONT_STYLES[style[0]]);
+                if (f == null) {
+                    f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                    style[0] = Typeface.NORMAL;
+                }
+                break;
+            case Typeface.BOLD_ITALIC:
+                f = getFont(ttf, FONT_STYLES[style[0]]);
+                if (f == null) {
+                    f = getFont(ttf, FONT_STYLES[Typeface.BOLD]);
+                    if (f != null) {
+                        style[0] = Typeface.BOLD;
+                    } else {
+                        f = getFont(ttf, FONT_STYLES[Typeface.ITALIC]);
+                        if (f != null) {
+                            style[0] = Typeface.ITALIC;
+                        } else {
+                            f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                            style[0] = Typeface.NORMAL;
+                        }
+                    }
+                }
+                break;
+        }
+
+        if (f != null) {
+            styleMap.put(style[0], f);
+            return f;
+        }
+
+        return null;
+    }
+
+    private Font getFont(String ttf, String[] fontFileSuffix) {
+        for (String suffix : fontFileSuffix) {
+            String name = ttf + suffix + FONT_EXT;
+            
+            File f = new File(name);
+            if (f.isFile()) {
+                try {
+                    Font font = Font.createFont(Font.TRUETYPE_FONT, f);
+                    if (font != null) {
+                        return font;
+                    }
+                } catch (FontFormatException e) {
+                    // skip this font name
+                } catch (IOException e) {
+                    // skip this font name
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    private final static class FontInfo {
+        String ttf;
+        final Set<String> families;
+        
+        FontInfo() {
+            families = new HashSet<String>();
+        }
+    }
+
+    private final static class FontDefinitionParser extends DefaultHandler {
+        private final String mOsFontsLocation;
+        
+        private int mDepth = 0;
+        private FontInfo mFontInfo = null;
+        private final StringBuilder mBuilder = new StringBuilder();
+        private final List<FontInfo> mFontList = new ArrayList<FontInfo>();
+        
+        private FontDefinitionParser(String osFontsLocation) {
+            super();
+            mOsFontsLocation = osFontsLocation;
+        }
+        
+        FontLoader getFontLoader() {
+            return new FontLoader(mFontList);
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+         */
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            if (localName.equals(NODE_LEVEL[mDepth])) {
+                mDepth++;
+                
+                if (mDepth == 2) { // font level.
+                    String ttf = attributes.getValue(ATTR_TTF);
+                    if (ttf != null) {
+                        mFontInfo = new FontInfo();
+                        mFontInfo.ttf = mOsFontsLocation + ttf;
+                        mFontList.add(mFontInfo);
+                    }
+                }
+            }
+
+            super.startElement(uri, localName, name, attributes);
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+         */
+        @SuppressWarnings("unused")
+        @Override
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            if (mFontInfo != null) {
+                mBuilder.append(ch, start, length);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+         */
+        @SuppressWarnings("unused")
+        @Override
+        public void endElement(String uri, String localName, String name) throws SAXException {
+            if (localName.equals(NODE_LEVEL[mDepth-1])) {
+                mDepth--;
+                if (mDepth == 2) { // end of a <name> node
+                    if (mFontInfo != null) {
+                        String family = trimXmlWhitespaces(mBuilder.toString());
+                        mFontInfo.families.add(family);
+                        mBuilder.setLength(0);
+                    }
+                } else if (mDepth == 1) { // end of a <font> node
+                    mFontInfo = null;
+                }
+            }
+        }
+        
+        private String trimXmlWhitespaces(String value) {
+            if (value == null) {
+                return null;
+            }
+
+            // look for carriage return and replace all whitespace around it by just 1 space.
+            int index;
+            
+            while ((index = value.indexOf('\n')) != -1) {
+                // look for whitespace on each side
+                int left = index - 1;
+                while (left >= 0) {
+                    if (Character.isWhitespace(value.charAt(left))) {
+                        left--;
+                    } else {
+                        break;
+                    }
+                }
+                
+                int right = index + 1;
+                int count = value.length();
+                while (right < count) {
+                    if (Character.isWhitespace(value.charAt(right))) {
+                        right++;
+                    } else {
+                        break;
+                    }
+                }
+                
+                // remove all between left and right (non inclusive) and replace by a single space.
+                String leftString = null;
+                if (left >= 0) {
+                    leftString = value.substring(0, left + 1);
+                }
+                String rightString = null;
+                if (right < count) {
+                    rightString = value.substring(right);
+                }
+                
+                if (leftString != null) {
+                    value = leftString;
+                    if (rightString != null) {
+                        value += " " + rightString;
+                    }
+                } else {
+                    value = rightString != null ? rightString : "";
+                }
+            }
+            
+            // now we un-escape the string
+            int length = value.length();
+            char[] buffer = value.toCharArray();
+            
+            for (int i = 0 ; i < length ; i++) {
+                if (buffer[i] == '\\') {
+                    if (buffer[i+1] == 'n') {
+                        // replace the char with \n
+                        buffer[i+1] = '\n';
+                    }
+                    
+                    // offset the rest of the buffer since we go from 2 to 1 char
+                    System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
+                    length--;
+                }
+            }
+            
+            return new String(buffer, 0, length);
+        }
+
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
new file mode 100644
index 0000000..c4c5225
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.ILayoutResult;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Implementation of {@link ILayoutResult}
+ */
+public final class LayoutResult implements ILayoutResult {
+
+    private final ILayoutViewInfo mRootView;
+    private final BufferedImage mImage;
+    private final int mSuccess;
+    private final String mErrorMessage;
+
+    /**
+     * Creates a {@link #SUCCESS} {@link ILayoutResult} with the specified params
+     * @param rootView
+     * @param image
+     */
+    public LayoutResult(ILayoutViewInfo rootView, BufferedImage image) {
+        mSuccess = SUCCESS;
+        mErrorMessage = null;
+        mRootView = rootView;
+        mImage = image;
+    }
+    
+    /**
+     * Creates a LayoutResult with a specific success code and associated message
+     * @param code
+     * @param message
+     */
+    public LayoutResult(int code, String message) {
+        mSuccess = code;
+        mErrorMessage = message;
+        mRootView = null;
+        mImage = null;
+    }
+
+    public int getSuccess() {
+        return mSuccess;
+    }
+
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    public BufferedImage getImage() {
+        return mImage;
+    }
+
+    public ILayoutViewInfo getRootView() {
+        return mRootView;
+    }
+    
+    /**
+     * Implementation of {@link ILayoutResult.ILayoutViewInfo}
+     */
+    public static final class LayoutViewInfo implements ILayoutViewInfo {
+        private final Object mKey;
+        private final String mName;
+        private final int mLeft;
+        private final int mRight;
+        private final int mTop;
+        private final int mBottom;
+        private ILayoutViewInfo[] mChildren;
+
+        public LayoutViewInfo(String name, Object key, int left, int top, int right, int bottom) {
+            mName = name;
+            mKey = key;
+            mLeft = left;
+            mRight = right;
+            mTop = top;
+            mBottom = bottom;
+        }
+        
+        public void setChildren(ILayoutViewInfo[] children) {
+            mChildren = children;
+        }
+
+        public ILayoutViewInfo[] getChildren() {
+            return mChildren;
+        }
+
+        public Object getViewKey() {
+            return mKey;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public int getLeft() {
+            return mLeft;
+        }
+
+        public int getTop() {
+            return mTop;
+        }
+
+        public int getRight() {
+            return mRight;
+        }
+
+        public int getBottom() {
+            return mBottom;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
new file mode 100644
index 0000000..1ca3182
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * Base class for mocked views.
+ * 
+ * TODO: implement onDraw and draw a rectangle in a random color with the name of the class
+ * (or better the id of the view).
+ */
+public class MockView extends TextView {
+    
+    public MockView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        
+        setText(this.getClass().getSimpleName());
+        setTextColor(0xFF000000);
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F);
+
+        super.onDraw(canvas);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
new file mode 100644
index 0000000..5f0852e
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.ninepatch.NinePatch;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class NinePatchDrawable extends Drawable {
+
+    private NinePatch m9Patch;
+
+    NinePatchDrawable(NinePatch ninePatch) {
+        m9Patch = ninePatch;
+    }
+    
+    @Override
+    public int getMinimumWidth() {
+        return m9Patch.getWidth();
+    }
+    
+    @Override
+    public int getMinimumHeight() {
+        return m9Patch.getHeight();
+    }
+    
+    /**
+     * Return the intrinsic width of the underlying drawable object.  Returns
+     * -1 if it has no intrinsic width, such as with a solid color.
+     */
+    @Override
+    public int getIntrinsicWidth() {
+        return m9Patch.getWidth();
+    }
+
+    /**
+     * Return the intrinsic height of the underlying drawable object. Returns
+     * -1 if it has no intrinsic height, such as with a solid color.
+     */
+    @Override
+    public int getIntrinsicHeight() {
+        return m9Patch.getHeight();
+    }
+    
+    /**
+     * Return in padding the insets suggested by this Drawable for placing
+     * content inside the drawable's bounds. Positive values move toward the
+     * center of the Drawable (set Rect.inset). Returns true if this drawable
+     * actually has a padding, else false. When false is returned, the padding
+     * is always set to 0.
+     */
+    @Override
+    public boolean getPadding(Rect padding) {
+        int[] padd = new int[4];
+        m9Patch.getPadding(padd);
+        padding.left = padd[0];
+        padding.top = padd[1];
+        padding.right = padd[2];
+        padding.bottom = padd[3];
+        return true;
+    }
+    
+    @Override
+    public void draw(Canvas canvas) {
+        if (canvas instanceof BridgeCanvas) {
+            BridgeCanvas bridgeCanvas = (BridgeCanvas)canvas;
+            
+            Rect r = getBounds();
+            m9Patch.draw(bridgeCanvas.getGraphics2d(), r.left, r.top, r.width(), r.height());
+            
+            return;
+        }
+
+        throw new UnsupportedOperationException();
+    }
+
+    
+    // ----------- Not implemented methods ---------------
+    
+
+    @Override
+    public int getOpacity() {
+        // FIXME
+        return 0xFF;
+    }
+
+    @Override
+    public void setAlpha(int arg0) {
+        // FIXME !
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter arg0) {
+        // FIXME
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
new file mode 100644
index 0000000..fbdf8dc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.ninepatch.NinePatch;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class to provide various convertion method used in handling android resources.
+ */
+public final class ResourceHelper {
+    
+    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
+    private final static float[] sFloatOut = new float[1];
+
+    private final static TypedValue mValue = new TypedValue();
+
+    /**
+     * Returns the color value represented by the given string value
+     * @param value the color value
+     * @return the color as an int
+     * @throw NumberFormatException if the conversion failed.
+     */
+    static int getColor(String value) {
+        if (value != null) {
+            if (value.startsWith("#") == false) {
+                throw new NumberFormatException();
+            }
+
+            value = value.substring(1);
+            
+            // make sure it's not longer than 32bit
+            if (value.length() > 8) {
+                throw new NumberFormatException();
+            }
+            
+            if (value.length() == 3) { // RGB format
+                char[] color = new char[8];
+                color[0] = color[1] = 'F';
+                color[2] = color[3] = value.charAt(0);
+                color[4] = color[5] = value.charAt(1);
+                color[6] = color[7] = value.charAt(2);
+                value = new String(color);
+            } else if (value.length() == 4) { // ARGB format
+                char[] color = new char[8];
+                color[0] = color[1] = value.charAt(0);
+                color[2] = color[3] = value.charAt(1);
+                color[4] = color[5] = value.charAt(2);
+                color[6] = color[7] = value.charAt(3);
+                value = new String(color);
+            } else if (value.length() == 6) {
+                value = "FF" + value;
+            }
+
+            // this is a RRGGBB or AARRGGBB value
+            
+            // Integer.parseInt will fail to parse strings like "ff191919", so we use
+            // a Long, but cast the result back into an int, since we know that we're only
+            // dealing with 32 bit values.
+            return (int)Long.parseLong(value, 16);
+        }
+
+        throw new NumberFormatException();
+    }
+
+    /**
+     * Returns a drawable from the given value.
+     * @param value The value. A path to a 9 patch, a bitmap or a xml based drawable,
+     * or an hexadecimal color
+     * @param context 
+     * @param isFramework indicates whether the resource is a framework resources.
+     * Framework resources are cached, and loaded only once.
+     */
+    public static Drawable getDrawable(String value, BridgeContext context, boolean isFramework) {
+        Drawable d = null;
+        
+        String lowerCaseValue = value.toLowerCase();
+
+        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
+            File f = new File(value);
+            if (f.isFile()) {
+                NinePatch ninePatch = Bridge.getCached9Patch(value,
+                        isFramework ? null : context.getProjectKey());
+                
+                if (ninePatch == null) {
+                    try {
+                        ninePatch = NinePatch.load(new File(value).toURL(), false /* convert */);
+                        
+                        Bridge.setCached9Patch(value, ninePatch,
+                                isFramework ? null : context.getProjectKey());
+                    } catch (MalformedURLException e) {
+                        // URL is wrong, we'll return null below
+                    } catch (IOException e) {
+                        // failed to read the file, we'll return null below.
+                    }
+                }
+                
+                if (ninePatch != null) {
+                    return new NinePatchDrawable(ninePatch);
+                }
+            }
+            
+            return null;
+        } else if (lowerCaseValue.endsWith(".xml")) {
+            // create a blockparser for the file
+            File f = new File(value);
+            if (f.isFile()) {
+                try {
+                    // let the framework inflate the Drawable from the XML file.
+                    KXmlParser parser = new KXmlParser();
+                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                    parser.setInput(new FileReader(f));
+                    
+                    d = Drawable.createFromXml(context.getResources(),
+                            // FIXME: we need to know if this resource is platform or not
+                            new BridgeXmlBlockParser(parser, context, false));
+                    return d;
+                } catch (XmlPullParserException e) {
+                    context.getLogger().error(e);
+                } catch (FileNotFoundException e) {
+                    // will not happen, since we pre-check
+                } catch (IOException e) {
+                    context.getLogger().error(e);
+                }
+            }
+
+            return null;
+        } else {
+            File bmpFile = new File(value);
+            if (bmpFile.isFile()) {
+                try {
+                    Bitmap bitmap = Bridge.getCachedBitmap(value,
+                            isFramework ? null : context.getProjectKey());
+                    
+                    if (bitmap == null) {
+                        bitmap = new Bitmap(bmpFile);
+                        Bridge.setCachedBitmap(value, bitmap,
+                                isFramework ? null : context.getProjectKey());
+                    }
+                    
+                    return new BitmapDrawable(bitmap);
+                } catch (IOException e) {
+                    // we'll return null below
+                    // TODO: log the error.
+                }
+            } else {
+                // attempt to get a color from the value
+                try {
+                    int color = getColor(value);
+                    return new ColorDrawable(color);
+                } catch (NumberFormatException e) {
+                    // we'll return null below.
+                    // TODO: log the error
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    
+    // ------- TypedValue stuff
+    // This is taken from //device/libs/utils/ResourceTypes.cpp
+    
+    private static final class UnitEntry {
+        String name;
+        int type;
+        int unit;
+        float scale;
+        
+        UnitEntry(String name, int type, int unit, float scale) {
+            this.name = name;
+            this.type = type;
+            this.unit = unit;
+            this.scale = scale;
+        }
+    }
+
+    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
+        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
+        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
+        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
+        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
+        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
+        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
+        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
+        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
+        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
+    };
+    
+    /**
+     * Returns the raw value from the given string.
+     * This object is only valid until the next call on to {@link ResourceHelper}.
+     */
+    public static TypedValue getValue(String s) {
+        if (stringToFloat(s, mValue)) {
+            return mValue;
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Convert the string into a {@link TypedValue}.
+     * @param s
+     * @param outValue
+     * @return true if success.
+     */
+    public static boolean stringToFloat(String s, TypedValue outValue) {
+        // remove the space before and after
+        s.trim();
+        int len = s.length();
+
+        if (len <= 0) {
+            return false;
+        }
+
+        // check that there's no non ascii characters.
+        char[] buf = s.toCharArray();
+        for (int i = 0 ; i < len ; i++) {
+            if (buf[i] > 255) {
+                return false;
+            }
+        }
+
+        // check the first character
+        if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
+            return false;
+        }
+        
+        // now look for the string that is after the float...
+        Matcher m = sFloatPattern.matcher(s);
+        if (m.matches()) {
+            String f_str = m.group(1);
+            String end = m.group(2);
+
+            float f;
+            try {
+                f = Float.parseFloat(f_str);
+            } catch (NumberFormatException e) {
+                // this shouldn't happen with the regexp above.
+                return false;
+            }
+            
+            if (end.length() > 0 && end.charAt(0) != ' ') {
+                // Might be a unit...
+                if (parseUnit(end, outValue, sFloatOut)) {
+                     
+                    f *= sFloatOut[0];
+                    boolean neg = f < 0;
+                    if (neg) {
+                        f = -f;
+                    }
+                    long bits = (long)(f*(1<<23)+.5f);
+                    int radix;
+                    int shift;
+                    if ((bits&0x7fffff) == 0) {
+                        // Always use 23p0 if there is no fraction, just to make
+                        // things easier to read.
+                        radix = TypedValue.COMPLEX_RADIX_23p0;
+                        shift = 23;
+                    } else if ((bits&0xffffffffff800000L) == 0) {
+                        // Magnitude is zero -- can fit in 0 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_0p23;
+                        shift = 0;
+                    } else if ((bits&0xffffffff80000000L) == 0) {
+                        // Magnitude can fit in 8 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_8p15;
+                        shift = 8;
+                    } else if ((bits&0xffffff8000000000L) == 0) {
+                        // Magnitude can fit in 16 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_16p7;
+                        shift = 16;
+                    } else {
+                        // Magnitude needs entire range, so no fractional part.
+                        radix = TypedValue.COMPLEX_RADIX_23p0;
+                        shift = 23;
+                    }
+                    int mantissa = (int)(
+                        (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
+                    if (neg) {
+                        mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
+                    }
+                    outValue.data |= 
+                        (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
+                        | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
+                    return true;
+                }
+                return false;
+            }
+            
+            // make sure it's only spaces at the end.
+            end = end.trim();
+    
+            if (end.length() == 0) {
+                if (outValue != null) {
+                    outValue.type = TypedValue.TYPE_FLOAT;
+                    outValue.data = Float.floatToIntBits(f);
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+    
+    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
+        str = str.trim();
+
+        for (UnitEntry unit : sUnitNames) {
+            if (unit.name.equals(str)) {
+                outValue.type = unit.type;
+                outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
+                outScale[0] = unit.scale;
+                
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java
new file mode 100644
index 0000000..01a4871
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.IResourceValue;
+
+/**
+ * Basic implementation of IResourceValue.
+ */
+class ResourceValue implements IResourceValue {
+    private final String mType;
+    private final String mName;
+    private String mValue = null;
+    
+    ResourceValue(String name) {
+        mType = null;
+        mName = name;
+    }
+
+    public ResourceValue(String type, String name, String value) {
+        mType = type;
+        mName = name;
+        mValue = value;
+    }
+
+    public String getType() {
+        return mType;
+    }
+
+    public final String getName() {
+        return mName;
+    }
+    
+    public final String getValue() {
+        return mValue;
+    }
+    
+    public final void setValue(String value) {
+        mValue = value;
+    }
+    
+    public void replaceWith(ResourceValue value) {
+        mValue = value.mValue;
+    }
+
+    public boolean isFramework() {
+        // ResourceValue object created directly in the framework are used to describe
+        // non resolvable coming from the XML. Since they will never be cached (as they can't
+        // be a value pointing to a bitmap, or they'd be resolvable.), the return value deoes
+        // not matter.
+        return false;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java
new file mode 100644
index 0000000..6d013bb
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.maps;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Mock version of the MapView.
+ * Only non override public methods from the real MapView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class MapView extends MockView {
+
+    /**
+     * Construct a new WebView with a Context object.
+     * @param context A Context object used to access application assets.
+     */
+    public MapView(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     */
+    public MapView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.mapViewStyle);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
+     */
+    public MapView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    // START FAKE PUBLIC METHODS
+    
+    public void displayZoomControls(boolean takeFocus) {
+    }
+
+    public boolean canCoverCenter() {
+        return false;
+    }
+
+    public void preLoad() {
+    }
+
+    public int getZoomLevel() {
+        return 0;
+    }
+
+    public void setSatellite(boolean on) {
+    }
+
+    public boolean isSatellite() {
+        return false;
+    }
+
+    public void setTraffic(boolean on) {
+    }
+
+    public boolean isTraffic() {
+        return false;
+    }
+
+    public void setStreetView(boolean on) {
+    }
+
+    public boolean isStreetView() {
+        return false;
+    }
+
+    public int getLatitudeSpan() {
+        return 0;
+    }
+
+    public int getLongitudeSpan() {
+        return 0;
+    }
+
+    public int getMaxZoomLevel() {
+        return 0;
+    }
+
+    public void onSaveInstanceState(Bundle state) {
+    }
+
+    public void onRestoreInstanceState(Bundle state) {
+    }
+
+    public View getZoomControls() {
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
new file mode 100644
index 0000000..ac144e7
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics._Original_Paint;
+import android.text.TextPaint;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ */
+public class AndroidGraphicsTests extends TestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testMatrix() {
+        Matrix m1 = new Matrix();
+        
+        assertFalse(m1.isIdentity());
+        
+        m1.setValues(new float[] { 1,0,0, 0,1,0, 0,0,1 });
+        assertTrue(m1.isIdentity());
+        
+        Matrix m2 = new Matrix(m1);
+        
+        float[] v1 = new float[9];
+        float[] v2 = new float[9];
+        m1.getValues(v1);
+        m2.getValues(v2);
+        
+        for (int i = 0 ; i < 9; i++) {
+            assertEquals(v1[i], v2[i]);
+        }
+    }
+    
+    public void testPaint() {
+        _Original_Paint o = new _Original_Paint();
+        assertNotNull(o);
+
+        Paint p = new Paint();
+        assertNotNull(p);
+    }
+    
+    public void textTextPaint() {
+        TextPaint p = new TextPaint();
+        assertNotNull(p);
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java
new file mode 100644
index 0000000..e424f1d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
+import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileReader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class BridgeTest extends TestCase {
+
+    /** the class being tested */
+    private Bridge mBridge;
+    /** the path to the sample layout.xml file */
+    private String mLayoutXml1Path;
+    private String mTextOnlyXmlPath;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mBridge = new Bridge();
+        
+        // FIXME: need some fonts somewhere.
+        mBridge.init(null /* fontOsLocation */, getAttributeValues());
+        
+        URL url = this.getClass().getClassLoader().getResource("data/layout1.xml");
+        mLayoutXml1Path = url.getFile();
+
+        url = this.getClass().getClassLoader().getResource("data/textonly.xml");
+        mTextOnlyXmlPath = url.getFile();
+    }
+    
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    // ---------------
+
+    /**
+     * Test parser that implements {@link IXmlPullParser}.
+     */
+    private static class TestParser extends KXmlParser implements IXmlPullParser {
+        public Object getViewKey() {
+            return null;
+        }
+    }
+
+    public void testComputeLayout() throws Exception {
+        
+        TestParser parser = new TestParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new FileReader(new File(mLayoutXml1Path)));
+
+        Map<String, Map<String, IResourceValue>> projectResources = getProjectResources();
+
+        Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources();
+        
+        int screenWidth = 320;
+        int screenHeight = 480;
+        
+        // FIXME need a dummy font for the tests!
+        ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, 
+                screenWidth, screenHeight,
+                "Theme", projectResources, frameworkResources, null, null);
+                
+        display(result.getRootView(), "");
+    }
+
+    private Map<String, Map<String, Integer>> getAttributeValues() {
+        Map<String, Map<String, Integer>> attributeValues =
+            new HashMap<String, Map<String,Integer>>();
+        
+        // lets create a map for the orientation attribute
+        Map<String, Integer> attributeMap = new HashMap<String, Integer>();
+        
+        attributeMap.put("horizontal", Integer.valueOf(0));
+        attributeMap.put("vertical", Integer.valueOf(1));
+        
+        attributeValues.put("orientation", attributeMap);
+        
+        return attributeValues;
+    }
+
+    private Map<String, Map<String, IResourceValue>> getFrameworkResources() {
+        Map<String, Map<String, IResourceValue>> frameworkResources =
+            new HashMap<String, Map<String, IResourceValue>>();
+        
+        // create the style map
+        Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>();
+        frameworkResources.put("style", styleMap);
+        
+        // create a button style.
+        IStyleResourceValue style = createStyle("Widget.Button",
+                "background",        "@android:drawable/something",
+                "focusable",        "true",
+                "clickable",        "true",
+                "textAppearance",   "?android:attr/textAppearanceSmallInverse",
+                "textColor",        "?android:attr/textColorBrightInverseNoDisable",
+                "gravity",          "center_vertical|center_horizontal"
+                );
+        styleMap.put(style.getName(), style);
+
+        // create the parent style of button style
+        style = createStyle("Widget",
+                "textAppearance", "?textAppearance");
+        styleMap.put(style.getName(), style);
+
+        // link the buttonStyle info in the default theme.
+        style = createStyle("Theme",
+                BridgeConstants.RES_STYLE, "buttonStyle",                      "@android:style/Widget.Button",
+                BridgeConstants.RES_STYLE, "textAppearance",                   "@android:style/TextAppearance",
+                BridgeConstants.RES_STYLE, "textAppearanceSmallInverse",       "@android:style/TextAppearance.Small.Inverse",
+                BridgeConstants.RES_COLOR, "textColorBrightInverseNoDisable",  "@android:color/bright_text_light_nodisable"
+                );
+        styleMap.put(style.getName(), style);
+
+        // create a dummy drawable to go with it
+        Map<String, IResourceValue> drawableMap = new HashMap<String, IResourceValue>();
+        frameworkResources.put("drawable", drawableMap);
+        
+        // get the 9 patch test location
+        URL url = this.getClass().getClassLoader().getResource("data/button.9.png");
+
+        IResourceValue drawable = new ResourceValue(BridgeConstants.RES_DRAWABLE, "something",
+                url.getPath());
+        drawableMap.put(drawable.getName(), drawable);
+        return frameworkResources;
+    }
+    
+    private Map<String, Map<String, IResourceValue>> getProjectResources() {
+        Map<String, Map<String, IResourceValue>> projectResources =
+            new HashMap<String, Map<String, IResourceValue>>();
+
+        // create the style map (even empty there should be one)
+        Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>();
+        projectResources.put("style", styleMap);
+
+        return projectResources;
+    }
+
+
+    private void display(ILayoutViewInfo result, String offset) {
+
+        String msg = String.format("%s%s L:%d T:%d R:%d B:%d",
+                offset,
+                result.getName(),
+                result.getLeft(), result.getTop(), result.getRight(), result.getBottom());
+
+        System.out.println(msg);
+        ILayoutViewInfo[] children = result.getChildren();
+        if (children != null) {
+            offset += "+-";
+            for (ILayoutViewInfo child : children) {
+                display(child, offset);
+            }
+        }
+    }
+    
+    /**
+     * Creates a {@link IStyleResourceValue} based on the given values.
+     * @param styleName the name of the style.
+     * @param items An array of Strings. Even indices contain a style item name, and odd indices
+     * a style item value. If the number of string in the array is not even, an exception is thrown.
+     */
+    private IStyleResourceValue createStyle(String styleName, String... items) {
+        StyleResourceValue value = new StyleResourceValue(styleName);
+        
+        if (items.length % 3 == 0) {
+            for (int i = 0 ; i < items.length;) {
+                value.addItem(new ResourceValue(items[i++], items[i++], items[i++]));
+            }
+        } else {
+            throw new IllegalArgumentException("Need a multiple of 3 for the number of strings");
+        }
+        
+        return value;
+    }
+
+    // ---------------
+
+    public void testTextLayout() throws Exception {
+        
+        TestParser parser = new TestParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new FileReader(new File(mTextOnlyXmlPath)));
+
+        Map<String, Map<String, IResourceValue>> projectResources = getProjectResources();
+        Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources();
+        
+        int screenWidth = 320;
+        int screenHeight = 480;
+
+        // FIXME need a dummy font for the tests!
+        ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */,
+                screenWidth, screenHeight,
+                "Theme", projectResources, frameworkResources, null, null);
+                
+        display(result.getRootView(), "");
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
new file mode 100644
index 0000000..cac1f95
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import org.kxml2.io.KXmlParser;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import junit.framework.TestCase;
+
+public class BridgeXmlBlockParserTest extends TestCase {
+
+    private String mXmlPath;
+    private Document mDoc;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        URL url = this.getClass().getClassLoader().getResource("data/layout1.xml");
+        mXmlPath = url.getFile();
+        mDoc = getXmlDocument(mXmlPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testXmlBlockParser() throws Exception {
+        XmlPullParser parser = new KXmlParser();
+        parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */);
+        parser.setInput(new FileReader(new File(mXmlPath)));
+
+        assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("LinearLayout", parser.getName());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("Button", parser.getName());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("View", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("TextView", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+        assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+    }
+    
+    //------------
+    
+    private Document getXmlDocument(String xmlFilePath)
+            throws ParserConfigurationException, SAXException, IOException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        
+        // keep comments
+        factory.setIgnoringComments(false);
+        // don't validate our bogus DTD
+        factory.setValidating(false);
+        // we want namespaces
+        factory.setNamespaceAware(true);
+        
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        return builder.parse(new File(xmlFilePath));
+    }
+
+
+    /**
+     * Quick'n'dirty debug helper that dumps an XML structure to stdout.
+     */
+    @SuppressWarnings("unused")
+    private void dump(Node node, String prefix) {
+        Node n;
+
+        String[] types = {
+                "unknown",
+                "ELEMENT_NODE",
+                "ATTRIBUTE_NODE",
+                "TEXT_NODE",
+                "CDATA_SECTION_NODE",
+                "ENTITY_REFERENCE_NODE",
+                "ENTITY_NODE",
+                "PROCESSING_INSTRUCTION_NODE",
+                "COMMENT_NODE",
+                "DOCUMENT_NODE",
+                "DOCUMENT_TYPE_NODE",
+                "DOCUMENT_FRAGMENT_NODE",
+                "NOTATION_NODE"
+        };
+        
+        String s = String.format("%s<%s> %s %s",
+                prefix,
+                types[node.getNodeType()],
+                node.getNodeName(),
+                node.getNodeValue() == null ? "" : node.getNodeValue().trim());
+
+        System.out.println(s);
+        
+        n = node.getFirstChild();
+        if (n != null) {
+            dump(n, prefix + "- ");
+        }
+
+        n = node.getNextSibling();
+        if (n != null) {
+            dump(n, prefix);
+        }
+
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
new file mode 100644
index 0000000..67ec5e1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
@@ -0,0 +1,35 @@
+package com.android.layoutlib.bridge;
+
+import com.android.ninepatch.NinePatch;
+
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+public class NinePatchTest extends TestCase {
+    
+    private NinePatch mPatch;
+
+    @Override
+    protected void setUp() throws Exception {
+        URL url = this.getClass().getClassLoader().getResource("data/button.9.png");
+
+        mPatch = NinePatch.load(url, false /* convert */);
+    }
+    
+    public void test9PatchLoad() throws Exception {
+        assertNotNull(mPatch);
+    }
+    
+    public void test9PatchMinSize() {
+        int[] padding = new int[4];
+        mPatch.getPadding(padding);
+        assertEquals(13, padding[0]);
+        assertEquals(3, padding[1]);
+        assertEquals(13, padding[2]);
+        assertEquals(4, padding[3]);
+        assertEquals(38, mPatch.getWidth());
+        assertEquals(27, mPatch.getHeight());
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java
new file mode 100644
index 0000000..84bdc2f
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import java.util.HashMap;
+
+class StyleResourceValue extends ResourceValue implements IStyleResourceValue {
+
+    private String mParentStyle = null;
+    private HashMap<String, IResourceValue> mItems = new HashMap<String, IResourceValue>();
+
+    StyleResourceValue(String name) {
+        super(name);
+    }
+
+    StyleResourceValue(String name, String parentStyle) {
+        super(name);
+        mParentStyle = parentStyle;
+    }
+
+    public String getParentStyle() {
+        return mParentStyle;
+    }
+    
+    public IResourceValue findItem(String name) {
+        return mItems.get(name);
+    }
+    
+    public void addItem(IResourceValue value) {
+        mItems.put(value.getName(), value);
+    }
+    
+    @Override
+    public void replaceWith(ResourceValue value) {
+        super.replaceWith(value);
+        
+        if (value instanceof StyleResourceValue) {
+            mItems.clear();
+            mItems.putAll(((StyleResourceValue)value).mItems);
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/data/button.9.png b/tools/layoutlib/bridge/tests/data/button.9.png
new file mode 100644
index 0000000..9d52f40
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/data/button.9.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/data/layout1.xml b/tools/layoutlib/bridge/tests/data/layout1.xml
new file mode 100644
index 0000000..554f541
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/data/layout1.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+	android:orientation="vertical"
+>
+	<Button
+		android:id="@+id/bouton"
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
+	    android:layout_weight="1"
+	    android:text="My Button Text"
+	    >
+	    </Button>
+	<View
+		android:id="@+id/surface"
+	    android:layout_width="fill_parent"
+	    android:layout_height="fill_parent"
+	    android:layout_weight="2"
+	    />
+	<TextView
+	    android:id="@+id/status"
+	    android:paddingLeft="2dip"
+	    android:layout_weight="0"
+	    android:background="@drawable/black"
+	    android:layout_width="fill_parent"
+	    android:layout_height="wrap_content"
+	    android:lines="1"
+	    android:gravity="center_vertical|center_horizontal"
+	    android:text="My TextView Text"
+	    />
+</LinearLayout>
diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath
new file mode 100644
index 0000000..0c60f6a
--- /dev/null
+++ b/tools/layoutlib/create/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry excluding="mock_android/" kind="src" path="tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/asm/asm-3.1.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/create/.project b/tools/layoutlib/create/.project
new file mode 100644
index 0000000..e100d17
--- /dev/null
+++ b/tools/layoutlib/create/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_create</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
new file mode 100644
index 0000000..310fae5
--- /dev/null
+++ b/tools/layoutlib/create/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_STATIC_JAVA_LIBRARIES := \
+	asm-3.1
+
+LOCAL_MODULE := layoutlib_create
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
new file mode 100644
index 0000000..09b392b
--- /dev/null
+++ b/tools/layoutlib/create/README.txt
@@ -0,0 +1,71 @@
+# Copyright (C) 2008 The Android Open Source Project
+
+
+- Description -
+---------------
+
+makeLayoutLib generates a library used by the Eclipse graphical layout editor
+to perform layout.
+
+
+
+- Usage -
+---------
+
+ ./makeLayoutLib path/to/android.jar destination.jar
+
+
+
+- Implementation Notes -
+------------------------
+
+The goal of makeLayoutLib is to list all the classes from the input jar and create a new
+jar that only keeps certain classes and create stubs for all their dependencies.
+
+First the input jar is parsed to find all the classes defined.
+
+In the Main(), the following list of classes are hardcoded (TODO config file later):
+- keep all classes that derive from android.view.View.
+- keep all classes in the android.view and android.widget packages (sub-packages excluded).
+- keep specific classes such as android.policy.PhoneLayoutInflater.
+
+For each class to keep, their dependencies are examined using BCEL.
+A dependency is defined as a class needed to instantiate the given class that should be kept,
+directly or indirectly. So a dependency is a class that is used by the input class, that is
+defined in the input jar and that is not part of the current JRE.
+
+Dependencies are computed recursively.
+
+Once all dependencies are found, the final jar can be created.
+There are three kind of classes to write:
+- classes that are to be kept as-is. They are just dumped in the new jar unchanged.
+- classes that are to be kept yet contain native methods or fields.
+- classes that are just dependencies. We don't want to expose their implementation in the final
+  jar.
+
+The implementation of native methods and all methods of mock classes is replaced by a stub
+that throws UnsupportedOperationException.
+
+Incidentally, the access level of native and mock classes needs to be changed in order for
+native methods to be later overridden. Methods that are "final private native" must become
+non-final, non-native and at most protected. Package-default access is changed to public.
+Classes that are final are made non-final. Abstract methods are left untouched.
+
+
+
+----
+20080617 Replace Class
+
+Some classes are basically wrappers over native objects.
+Subclassing doesn't work as most methods are either static or we don't
+control object creation. In this scenario the idea is to be able to
+replace classes in the final jar.
+
+Example: android.graphics.Paint would get renamed to OriginalPaint
+in the generated jar. Then in the bridge we'll introduce a replacement
+Paint class that derives from OriginalPaint.
+
+This won't rename/replace the inner static methods of a given class.
+
+
+
diff --git a/tools/layoutlib/create/manifest.txt b/tools/layoutlib/create/manifest.txt
new file mode 100644
index 0000000..238e7f9
--- /dev/null
+++ b/tools/layoutlib/create/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.tools.layoutlib.create.Main
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
new file mode 100644
index 0000000..b197ea7
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Analyzes the input JAR using the ASM java bytecode manipulation library
+ * to list the desired classes and their dependencies.
+ */
+public class AsmAnalyzer {
+
+    // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
+    
+    /** Output logger. */
+    private final Log mLog;
+    /** The input source JAR to parse. */
+    private final List<String> mOsSourceJar;
+    /** The generator to fill with the class list and dependency list. */
+    private final AsmGenerator mGen;
+    /** Keep all classes that derive from these one (these included). */
+    private final String[] mDeriveFrom;
+    /** Glob patterns of classes to keep, e.g. "com.foo.*" */
+    private final String[] mIncludeGlobs;
+
+    /**
+     * Creates a new analyzer.
+     * 
+     * @param log The log output.
+     * @param osJarPath The input source JARs to parse.
+     * @param gen The generator to fill with the class list and dependency list.
+     * @param deriveFrom Keep all classes that derive from these one (these included). 
+     * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
+     *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
+     */
+    public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
+            String[] deriveFrom, String[] includeGlobs) {
+        mLog = log;
+        mGen = gen;
+        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
+        mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
+        mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
+    }
+
+    /**
+     * Starts the analysis using parameters from the constructor.
+     * Fills the generator with classes & dependencies found.
+     */
+    public void analyze() throws IOException, LogAbortException {
+
+        AsmAnalyzer visitor = this;
+        
+        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
+        mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
+                mOsSourceJar.size() > 1 ? "s" : "");
+        
+        Map<String, ClassReader> found = findIncludes(zipClasses);
+        Map<String, ClassReader> deps = findDeps(zipClasses, found);
+        
+        if (mGen != null) {
+            mGen.setKeep(found);
+            mGen.setDeps(deps);
+        }
+    }
+
+    /**
+     * Parses a JAR file and returns a list of all classes founds using a map
+     * class name => ASM ClassReader. Class names are in the form "android.view.View".
+     */
+    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
+        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+        for (String jarPath : jarPathList) {
+            ZipFile zip = new ZipFile(jarPath);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            ZipEntry entry;
+            while (entries.hasMoreElements()) {
+                entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                    String className = classReaderToClassName(cr);
+                    classes.put(className, cr);
+                }
+            }
+        }
+        
+        return classes;
+    }
+    
+    /**
+     * Utility that returns the fully qualified binary class name for a ClassReader.
+     * E.g. it returns something like android.view.View.
+     */
+    static String classReaderToClassName(ClassReader classReader) {
+        if (classReader == null) {
+            return null;
+        } else {
+            return classReader.getClassName().replace('/', '.');
+        }
+    }
+    
+    /**
+     * Utility that returns the fully qualified binary class name from a path-like FQCN.
+     * E.g. it returns android.view.View from android/view/View.
+     */
+    static String internalToBinaryClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('/', '.');
+        }
+    }
+    
+    /**
+     * Process the "includes" arrays.
+     * <p/>
+     * This updates the in_out_found map.
+     */
+    Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
+            throws LogAbortException {
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mLog.debug("Find classes to include.");
+
+        for (String s : mIncludeGlobs) {
+            findGlobs(s, zipClasses, found);
+        }
+        for (String s : mDeriveFrom) {
+            findClassesDerivingFrom(s, zipClasses, found);
+        }
+        
+        return found;
+    }
+
+    
+    /**
+     * Uses ASM to find the class reader for the given FQCN class name.
+     * If found, insert it in the in_out_found map.
+     * Returns the class reader object.
+     */
+    ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader classReader = zipClasses.get(className);
+        if (classReader == null) {
+            throw new LogAbortException("Class %s not found by ASM in %s",
+                    className, mOsSourceJar);
+        }
+
+        inOutFound.put(className, classReader);
+        return classReader;
+    }
+
+    /**
+     * Insert in the inOutFound map all classes found in zipClasses that match the
+     * given glob pattern.
+     * <p/>
+     * The glob pattern is not a regexp. It only accepts the "*" keyword to mean
+     * "anything but a period". The "." and "$" characters match themselves.
+     * The "**" keyword means everything including ".".
+     * <p/>
+     * Examples:
+     * <ul>
+     * <li>com.foo.* matches all classes in the package com.foo but NOT sub-packages.
+     * <li>com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.
+     * </ul>
+     */
+    void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        // transforms the glob pattern in a regexp:
+        // - escape "." with "\."
+        // - replace "*" by "[^.]*"
+        // - escape "$" with "\$"
+        // - add end-of-line match $
+        globPattern = globPattern.replaceAll("\\$", "\\\\\\$");
+        globPattern = globPattern.replaceAll("\\.", "\\\\.");
+        // prevent ** from being altered by the next rule, then process the * rule and finally
+        // the real ** rule (which is now @)
+        globPattern = globPattern.replaceAll("\\*\\*", "@");
+        globPattern = globPattern.replaceAll("\\*", "[^.]*");
+        globPattern = globPattern.replaceAll("@", ".*");
+        globPattern += "$";
+
+        Pattern regexp = Pattern.compile(globPattern);
+        
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String class_name = entry.getKey();
+            if (regexp.matcher(class_name).matches()) {
+                findClass(class_name, zipClasses, inOutFound);
+            }
+        }
+    }
+
+    /**
+     * Checks all the classes defined in the JarClassName instance and uses BCEL to
+     * determine if they are derived from the given FQCN super class name.
+     * Inserts the super class and all the class objects found in the map.
+     */
+    void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);
+
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String className = entry.getKey();
+            if (super_name.equals(className)) {
+                continue;
+            }
+            ClassReader classReader = entry.getValue();
+            ClassReader parent_cr = classReader;
+            while (parent_cr != null) {
+                String parent_name = internalToBinaryClassName(parent_cr.getSuperName());
+                if (parent_name == null) {
+                    // not found
+                    break;
+                } else if (super_name.equals(parent_name)) {
+                    inOutFound.put(className, classReader);
+                    break;
+                }
+                parent_cr = zipClasses.get(parent_name);
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new DependencyVisitor. Useful for unit tests.
+     */
+    DependencyVisitor getVisitor(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inKeep,
+            Map<String, ClassReader> outKeep,
+            Map<String, ClassReader> inDeps,
+            Map<String, ClassReader> outDeps) {
+        return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps);
+    }
+
+    /**
+     * Finds all dependencies for all classes in keepClasses which are also
+     * listed in zipClasses. Returns a map of all the dependencies found.
+     */
+    Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutKeepClasses) {
+
+        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();
+
+        DependencyVisitor visitor = getVisitor(zipClasses,
+                inOutKeepClasses, new_keep,
+                deps, new_deps);
+
+        for (ClassReader cr : inOutKeepClasses.values()) {
+            cr.accept(visitor, 0 /* flags */);
+        }
+        
+        while (new_deps.size() > 0 || new_keep.size() > 0) {
+            deps.putAll(new_deps);
+            inOutKeepClasses.putAll(new_keep);
+
+            temp.clear();
+            temp.putAll(new_deps);
+            temp.putAll(new_keep);
+            new_deps.clear();
+            new_keep.clear();
+            mLog.debug("Found %1$d to keep, %2$d dependencies.",
+                    inOutKeepClasses.size(), deps.size());
+
+            for (ClassReader cr : temp.values()) {
+                cr.accept(visitor, 0 /* flags */);
+            }
+        }
+
+        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
+                inOutKeepClasses.size(), deps.size());
+
+        return deps;
+    }
+
+    
+
+    // ----------------------------------
+    
+    /**
+     * Visitor to collect all the type dependencies from a class. 
+     */
+    public class DependencyVisitor
+        implements ClassVisitor, FieldVisitor, MethodVisitor, SignatureVisitor, AnnotationVisitor {
+
+        /** All classes found in the source JAR. */
+        private final Map<String, ClassReader> mZipClasses;
+        /** Classes from which dependencies are to be found. */
+        private final Map<String, ClassReader> mInKeep;
+        /** Dependencies already known. */
+        private final Map<String, ClassReader> mInDeps;
+        /** New dependencies found by this visitor. */
+        private final Map<String, ClassReader> mOutDeps;
+        /** New classes to keep as-is found by this visitor. */
+        private final Map<String, ClassReader> mOutKeep;
+
+        /**
+         * Creates a new visitor that will find all the dependencies for the visited class.
+         * Types which are already in the zipClasses, keepClasses or inDeps are not marked.
+         * New dependencies are marked in outDeps.
+         * 
+         * @param zipClasses All classes found in the source JAR.
+         * @param inKeep Classes from which dependencies are to be found.
+         * @param inDeps Dependencies already known.
+         * @param outDeps New dependencies found by this visitor.
+         */
+        public DependencyVisitor(Map<String, ClassReader> zipClasses,
+                Map<String, ClassReader> inKeep,
+                Map<String, ClassReader> outKeep,
+                Map<String,ClassReader> inDeps,
+                Map<String,ClassReader> outDeps) {
+            mZipClasses = zipClasses;
+            mInKeep = inKeep;
+            mOutKeep = outKeep;
+            mInDeps = inDeps;
+            mOutDeps = outDeps;
+        }
+        
+        /**
+         * Considers the given class name as a dependency.
+         * If it does, add to the mOutDeps map.
+         */
+        public void considerName(String className) {
+            if (className == null) {
+                return;
+            }
+
+            className = internalToBinaryClassName(className);
+            
+            // exclude classes that have already been found
+            if (mInKeep.containsKey(className) ||
+                    mOutKeep.containsKey(className) ||
+                    mInDeps.containsKey(className) ||
+                    mOutDeps.containsKey(className)) {
+                return;
+            }
+
+            // exclude classes that are not part of the JAR file being examined
+            ClassReader cr = mZipClasses.get(className);
+            if (cr == null) {
+                return;
+            }
+
+            try {
+                // exclude classes that are part of the default JRE (the one executing this program)
+                if (getClass().getClassLoader().loadClass(className) != null) {
+                    return;
+                }
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
+            
+            // accept this class:
+            // - android classes are added to dependencies
+            // - non-android classes are added to the list of classes to keep as-is (they don't need
+            //   to be stubbed).
+            if (className.indexOf("android") >= 0) {  // TODO make configurable
+                mOutDeps.put(className, cr);
+            } else {
+                mOutKeep.put(className, cr);
+            }
+        }
+        
+        /**
+         * Considers this array of names using considerName().
+         */
+        public void considerNames(String[] classNames) {
+            if (classNames != null) {
+                for (String className : classNames) {
+                    considerName(className);
+                }
+            }
+        }
+
+        /**
+         * Considers this signature or type signature by invoking the {@link SignatureVisitor}
+         * on it.
+         */
+        public void considerSignature(String signature) {
+            if (signature != null) {
+                SignatureReader sr = new SignatureReader(signature);
+                // SignatureReader.accept will call accessType so we don't really have
+                // to differentiate where the signature comes from.
+                sr.accept(this);
+            }
+        }
+
+        /**
+         * Considers this {@link Type}. For arrays, the element type is considered.
+         * If the type is an object, it's internal name is considered.
+         */
+        public void considerType(Type t) {
+            if (t != null) {
+                if (t.getSort() == Type.ARRAY) {
+                    t = t.getElementType();
+                }
+                if (t.getSort() == Type.OBJECT) {
+                    considerName(t.getInternalName());
+                }
+            }
+        }
+
+        /**
+         * Considers a descriptor string. The descriptor is converted to a {@link Type}
+         * and then considerType() is invoked.
+         */
+        public void considerDesc(String desc) {
+            if (desc != null) {
+                try {
+                    Type t = Type.getType(desc);
+                    considerType(t);
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // ignore, not a valid type.
+                }
+            }
+        }
+
+        
+        // ---------------------------------------------------
+        // --- ClassVisitor, FieldVisitor
+        // ---------------------------------------------------
+
+        // Visits a class header
+        public void visit(int version, int access, String name,
+                String signature, String superName, String[] interfaces) {
+            // signature is the signature of this class. May be null if the class is not a generic
+            // one, and does not extend or implement generic classes or interfaces.
+            
+            if (signature != null) {
+                considerSignature(signature);
+            }
+
+            // superName is the internal of name of the super class (see getInternalName).
+            // For interfaces, the super class is Object. May be null but only for the Object class.
+            considerName(superName);
+            
+            // interfaces is the internal names of the class's interfaces (see getInternalName).
+            // May be null.
+            considerNames(interfaces);
+        }
+
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return this; // return this to visit annotion values
+        }
+
+        public void visitAttribute(Attribute attr) {
+            // pass
+        }
+
+        // Visits the end of a class
+        public void visitEnd() {
+            // pass
+        }
+
+        public FieldVisitor visitField(int access, String name, String desc,
+                String signature, Object value) {
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+
+            // signature is the field's signature. May be null if the field's type does not use
+            // generic types.
+            considerSignature(signature);
+
+            return this; // a visitor to visit field annotations and attributes
+        }
+
+        public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            // name is the internal name of an inner class (see getInternalName).
+            considerName(name);
+        }
+
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                String signature, String[] exceptions) {
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+            // signature is the method's signature. May be null if the method parameters, return
+            // type and exceptions do not use generic types.
+            considerSignature(signature);
+            
+            return this; // returns this to visit the method
+        }
+
+        public void visitOuterClass(String owner, String name, String desc) {
+            // pass
+        }
+
+        public void visitSource(String source, String debug) {
+            // pass
+        }
+
+        
+        // ---------------------------------------------------
+        // --- MethodVisitor
+        // ---------------------------------------------------
+
+        public AnnotationVisitor visitAnnotationDefault() {
+            return this; // returns this to visit the default value
+        }
+
+
+        public void visitCode() {
+            // pass
+        }
+
+        // field instruction
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            // name is the field's name.
+            considerName(name);
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+        }
+
+        public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+            // pass
+        }
+
+        public void visitIincInsn(int var, int increment) {
+            // pass -- an IINC instruction
+        }
+
+        public void visitInsn(int opcode) {
+            // pass -- a zero operand instruction
+        }
+
+        public void visitIntInsn(int opcode, int operand) {
+            // pass -- a single int operand instruction
+        }
+
+        public void visitJumpInsn(int opcode, Label label) {
+            // pass -- a jump instruction
+        }
+
+        public void visitLabel(Label label) {
+            // pass -- a label target
+        }
+
+        // instruction to load a constant from the stack
+        public void visitLdcInsn(Object cst) {
+            if (cst instanceof Type) {
+                considerType((Type) cst);
+            }
+        }
+
+        public void visitLineNumber(int line, Label start) {
+            // pass
+        }
+
+        public void visitLocalVariable(String name, String desc,
+                String signature, Label start, Label end, int index) {
+            // desc is the type descriptor of this local variable.
+            considerDesc(desc);
+            // signature is the type signature of this local variable. May be null if the local
+            // variable type does not use generic types.
+            considerSignature(signature);
+        }
+
+        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+            // pass -- a lookup switch instruction
+        }
+
+        public void visitMaxs(int maxStack, int maxLocals) {
+            // pass
+        }
+
+        // instruction that invokes a method
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            
+            // owner is the internal name of the method's owner class
+            considerName(owner);
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+        }
+
+        // instruction multianewarray, whatever that is
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            
+            // desc an array type descriptor.
+            considerDesc(desc);
+        }
+
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+                boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return this; // return this to visit annotation values
+        }
+
+        public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+            // pass -- table switch instruction
+            
+        }
+
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            // type is the internal name of the type of exceptions handled by the handler,
+            // or null to catch any exceptions (for "finally" blocks).
+            considerName(type);
+        }
+
+        // type instruction
+        public void visitTypeInsn(int opcode, String type) {
+            // type is the operand of the instruction to be visited. This operand must be the
+            // internal name of an object or array class.
+            considerName(type);
+        }
+
+        public void visitVarInsn(int opcode, int var) {
+            // pass -- local variable instruction 
+        }
+
+        
+        // ---------------------------------------------------
+        // --- SignatureVisitor
+        // ---------------------------------------------------
+
+        private String mCurrentSignatureClass = null;
+
+        // Starts the visit of a signature corresponding to a class or interface type
+        public void visitClassType(String name) {
+            mCurrentSignatureClass = name;
+            considerName(name);
+        }
+
+        // Visits an inner class
+        public void visitInnerClassType(String name) {
+            if (mCurrentSignatureClass != null) {
+                mCurrentSignatureClass += "$" + name;
+                considerName(mCurrentSignatureClass);
+            }
+        }
+
+        public SignatureVisitor visitArrayType() {
+            return this; // returns this to visit the signature of the array element type
+        }
+
+        public void visitBaseType(char descriptor) {
+            // pass -- a primitive type, ignored
+        }
+
+        public SignatureVisitor visitClassBound() {
+            return this; // returns this to visit the signature of the class bound
+        }
+
+        public SignatureVisitor visitExceptionType() {
+            return this; // return this to visit the signature of the exception type.
+        }
+
+        public void visitFormalTypeParameter(String name) {
+            // pass
+        }
+
+        public SignatureVisitor visitInterface() {
+            return this; // returns this to visit the signature of the interface type
+        }
+
+        public SignatureVisitor visitInterfaceBound() {
+            return this; // returns this to visit the signature of the interface bound
+        }
+
+        public SignatureVisitor visitParameterType() {
+            return this; // returns this to visit the signature of the parameter type
+        }
+
+        public SignatureVisitor visitReturnType() {
+            return this; // returns this to visit the signature of the return type
+        }
+
+        public SignatureVisitor visitSuperclass() {
+            return this; // returns this to visit the signature of the super class type
+        }
+
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            return this; // returns this to visit the signature of the type argument
+        }
+
+        public void visitTypeVariable(String name) {
+            // pass
+        }
+
+        public void visitTypeArgument() {
+            // pass
+        }
+        
+        
+        // ---------------------------------------------------
+        // --- AnnotationVisitor
+        // ---------------------------------------------------
+
+
+        // Visits a primitive value of an annotation
+        public void visit(String name, Object value) {
+            // value is the actual value, whose type must be Byte, Boolean, Character, Short,
+            // Integer, Long, Float, Double, String or Type
+            if (value instanceof Type) {
+                considerType((Type) value);
+            }
+        }
+
+        public AnnotationVisitor visitAnnotation(String name, String desc) {
+            // desc is the class descriptor of the nested annotation class.
+            considerDesc(desc);
+            return this; // returns this to visit the actual nested annotation value
+        }
+
+        public AnnotationVisitor visitArray(String name) {
+            return this; // returns this to visit the actual array value elements
+        }
+
+        public void visitEnum(String name, String desc, String value) {
+            // desc is the class descriptor of the enumeration class.
+            considerDesc(desc);
+        }
+        
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
new file mode 100644
index 0000000..1adcc17
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+/**
+ * Class that generates a new JAR from a list of classes, some of which are to be kept as-is
+ * and some of which are to be stubbed partially or totally.
+ */
+public class AsmGenerator {
+
+    /** Output logger. */
+    private final Log mLog;
+    /** The path of the destination JAR to create. */
+    private final String mOsDestJar;
+    /** List of classes to inject in the final JAR from _this_ archive. */
+    private final Class<?>[] mInjectClasses;
+    /** The set of methods to stub out. */
+    private final Set<String> mStubMethods;
+    /** All classes to output as-is, except if they have native methods. */
+    private Map<String, ClassReader> mKeep;
+    /** All dependencies that must be completely stubbed. */
+    private Map<String, ClassReader> mDeps;
+    /** Counter of number of classes renamed during transform. */
+    private int mRenameCount;
+    /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
+    private final HashMap<String, String> mRenameClasses;
+    /** FQCN Names of "old" classes that were NOT renamed. This starts with the full list of
+     *  old-FQCN to rename and they get erased as they get renamed. At the end, classes still
+     *  left here are not in the code base anymore and thus were not renamed. */
+    private HashSet<String> mClassesNotRenamed;
+    /** A map { FQCN => map { list of return types to delete from the FQCN } }. */
+    private HashMap<String, Set<String>> mDeleteReturns;
+
+    /**
+     * Creates a new generator that can generate the output JAR with the stubbed classes.
+     * 
+     * @param log Output logger.
+     * @param osDestJar The path of the destination JAR to create.
+     * @param stubMethods The list of methods to stub out. Each entry must be in the form
+     *          "package.package.OuterClass$InnerClass#MethodName".
+     * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN
+     *          of class to replace followed by the new FQCN.
+     * @param deleteReturns List of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     */
+    public AsmGenerator(Log log, String osDestJar,
+            Class<?>[] injectClasses,
+            String[] stubMethods,
+            String[] renameClasses, String[] deleteReturns) {
+        mLog = log;
+        mOsDestJar = osDestJar;
+        mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0];
+        mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) :
+                                             new HashSet<String>();
+
+        // Create the map of classes to rename.
+        mRenameClasses = new HashMap<String, String>();
+        mClassesNotRenamed = new HashSet<String>();
+        int n = renameClasses == null ? 0 : renameClasses.length;
+        for (int i = 0; i < n; i += 2) {
+            assert i + 1 < n;
+            // The ASM class names uses "/" separators, whereas regular FQCN use "."
+            String oldFqcn = binaryToInternalClassName(renameClasses[i]);
+            String newFqcn = binaryToInternalClassName(renameClasses[i + 1]);
+            mRenameClasses.put(oldFqcn, newFqcn);
+            mClassesNotRenamed.add(oldFqcn);
+        }
+        
+        // create the map of renamed class -> return type of method to delete.
+        mDeleteReturns = new HashMap<String, Set<String>>();
+        if (deleteReturns != null) {
+            Set<String> returnTypes = null;
+            String renamedClass = null;
+            for (String className : deleteReturns) {
+                // if we reach the end of a section, add it to the main map
+                if (className == null) {
+                    if (returnTypes != null) {
+                        mDeleteReturns.put(renamedClass, returnTypes);
+                    }
+                    
+                    renamedClass = null;
+                    continue;
+                }
+    
+                // if the renamed class is null, this is the beginning of a section
+                if (renamedClass == null) {
+                    renamedClass = binaryToInternalClassName(className);
+                    continue;
+                }
+    
+                // just a standard return type, we add it to the list.
+                if (returnTypes == null) {
+                    returnTypes = new HashSet<String>();
+                }
+                returnTypes.add(binaryToInternalClassName(className));
+            }
+        }
+    }
+    
+    /**
+     * Returns the list of classes that have not been renamed yet.
+     * <p/>
+     * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "."
+     * as package separators.
+     */
+    public Set<String> getClassesNotRenamed() {
+        return mClassesNotRenamed;
+    }
+
+    /**
+     * Utility that returns the internal ASM class name from a fully qualified binary class
+     * name. E.g. it returns android/view/View from android.view.View.
+     */
+    String binaryToInternalClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('.', '/');
+        }
+    }
+
+    /** Sets the map of classes to output as-is, except if they have native methods */
+    public void setKeep(Map<String, ClassReader> keep) {
+        mKeep = keep;
+    }
+
+    /** Sets the map of dependencies that must be completely stubbed */
+    public void setDeps(Map<String, ClassReader> deps) {
+        mDeps = deps;
+    }
+    
+    /** Gets the map of classes to output as-is, except if they have native methods */
+    public Map<String, ClassReader> getKeep() {
+        return mKeep;
+    }
+    
+    /** Gets the map of dependencies that must be completely stubbed */
+    public Map<String, ClassReader> getDeps() {
+        return mDeps;
+    }
+
+    /** Generates the final JAR */
+    public void generate() throws FileNotFoundException, IOException {
+        TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
+        
+        for (Class<?> clazz : mInjectClasses) {
+            String name = classToEntryPath(clazz);
+            InputStream is = ClassLoader.getSystemResourceAsStream(name);
+            ClassReader cr = new ClassReader(is);
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+        
+        for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
+            ClassReader cr = entry.getValue();
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        for (Entry<String, ClassReader> entry : mKeep.entrySet()) {
+            ClassReader cr = entry.getValue();
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        mLog.info("# deps classes: %d", mDeps.size());
+        mLog.info("# keep classes: %d", mKeep.size());
+        mLog.info("# renamed     : %d", mRenameCount);
+
+        createJar(new FileOutputStream(mOsDestJar), all);
+        mLog.info("Created JAR file %s", mOsDestJar);
+    }
+
+    /**
+     * Writes the JAR file.
+     * 
+     * @param outStream The file output stream were to write the JAR. 
+     * @param all The map of all classes to output.
+     * @throws IOException if an I/O error has occurred
+     */
+    void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
+        JarOutputStream jar = new JarOutputStream(outStream);
+        for (Entry<String, byte[]> entry : all.entrySet()) {
+            String name = entry.getKey();
+            JarEntry jar_entry = new JarEntry(name);
+            jar.putNextEntry(jar_entry);
+            jar.write(entry.getValue());
+            jar.closeEntry();
+        }
+        jar.flush();
+        jar.close();
+    }
+
+    /**
+     * Utility method that converts a fully qualified java name into a JAR entry path
+     * e.g. for the input "android.view.View" it returns "android/view/View.class"
+     */
+    String classNameToEntryPath(String className) {
+        return className.replaceAll("\\.", "/").concat(".class");
+    }
+    
+    /**
+     * Utility method to get the JAR entry path from a Class name.
+     * e.g. it returns someting like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
+     */
+    private String classToEntryPath(Class<?> clazz) {
+        String name = "";
+        Class<?> parent;
+        while ((parent = clazz.getEnclosingClass()) != null) {
+            name = "$" + clazz.getSimpleName() + name;
+            clazz = parent;
+        }
+        return classNameToEntryPath(clazz.getCanonicalName() + name);        
+    }
+
+    /**
+     * Transforms a class.
+     * <p/>
+     * There are 3 kind of transformations:
+     * 
+     * 1- For "mock" dependencies classes, we want to remove all code from methods and replace
+     * by a stub. Native methods must be implemented with this stub too. Abstract methods are
+     * left intact. Modified classes must be overridable (non-private, non-final).
+     * Native methods must be made non-final, non-private.
+     * 
+     * 2- For "keep" classes, we want to rewrite all native methods as indicated above.
+     * If a class has native methods, it must also be made non-private, non-final.
+     * 
+     * Note that unfortunately static methods cannot be changed to non-static (since static and
+     * non-static are invoked differently.)
+     */
+    byte[] transform(ClassReader cr, boolean stubNativesOnly) {
+
+        boolean hasNativeMethods = hasNativeMethods(cr);
+        String className = cr.getClassName();
+        
+        String newName = transformName(className);
+        // transformName returns its input argument if there's no need to rename the class
+        if (newName != className) {
+            mRenameCount++;
+            // This class is being renamed, so remove it from the list of classes not renamed.
+            mClassesNotRenamed.remove(className);
+        }
+
+        mLog.debug("Transform %s%s%s%s", className,
+                newName == className ? "" : " (renamed to " + newName + ")",
+                hasNativeMethods ? " -- has natives" : "",
+                stubNativesOnly ? " -- stub natives only" : "");
+
+        // Rewrite the new class from scratch, without reusing the constant pool from the
+        // original class reader.
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        
+        ClassVisitor rv = cw;
+        if (newName != className) {
+            rv = new RenameClassAdapter(cw, className, newName);
+        }
+        
+        TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, 
+                mDeleteReturns.get(className),
+                newName, rv,
+                stubNativesOnly, stubNativesOnly || hasNativeMethods);
+        cr.accept(cv, 0 /* flags */);
+        return cw.toByteArray();
+    }
+
+    /**
+     * Should this class be renamed, this returns the new name. Otherwise it returns the
+     * original name.
+     *
+     * @param className The internal ASM name of the class that may have to be renamed
+     * @return A new transformed name or the original input argument.
+     */
+    String transformName(String className) {
+        String newName = mRenameClasses.get(className);
+        if (newName != null) {
+            return newName;
+        }
+        int pos = className.indexOf('$');
+        if (pos > 0) {
+            // Is this an inner class of a renamed class?
+            String base = className.substring(0, pos);
+            newName = mRenameClasses.get(base);
+            if (newName != null) {
+                return newName + className.substring(pos);
+            }
+        }
+        
+        return className;
+    }
+
+    /**
+     * Returns true if a class has any native methods.
+     */
+    boolean hasNativeMethods(ClassReader cr) {
+        ClassHasNativeVisitor cv = new ClassHasNativeVisitor();
+        cr.accept(cv, 0 /* flags */);
+        return cv.hasNativeMethods();
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
new file mode 100644
index 0000000..5424efa
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Indicates if a class contains any native methods.
+ */
+public class ClassHasNativeVisitor implements ClassVisitor {
+    
+    private boolean mHasNativeMethods = false;
+    
+    public boolean hasNativeMethods() {
+        return mHasNativeMethods;
+    }
+
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        // pass
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        // pass
+        return null;
+    }
+
+    public void visitAttribute(Attribute attr) {
+        // pass
+    }
+
+    public void visitEnd() {
+        // pass
+    }
+
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        // pass
+        return null;
+    }
+
+    public void visitInnerClass(String name, String outerName,
+            String innerName, int access) {
+        // pass
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0);
+        return null;
+    }
+
+    public void visitOuterClass(String owner, String name, String desc) {
+        // pass
+    }
+
+    public void visitSource(String source, String debug) {
+        // pass
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
new file mode 100644
index 0000000..8efd871
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class Log {
+
+    private boolean mVerbose = false;
+
+    public void setVerbose(boolean verbose) {
+        mVerbose = verbose;
+    }
+
+    public void debug(String format, Object... args) {
+        if (mVerbose) {
+            info(format, args);
+        }
+    }
+
+    public void info(String format, Object... args) {
+        String s = String.format(format, args);
+        outPrintln(s);
+    }
+    
+    public void error(String format, Object... args) {
+        String s = String.format(format, args);
+        errPrintln(s);
+    }
+
+    public void exception(Throwable t, String format, Object... args) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        t.printStackTrace(pw);
+        pw.flush();
+        error(format + "\n" + sw.toString(), args);
+    }
+    
+    /** for unit testing */
+    protected void errPrintln(String msg) {
+        System.err.println(msg);
+    }
+    
+    /** for unit testing */
+    protected void outPrintln(String msg) {
+        System.out.println(msg);
+    }
+    
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
new file mode 100644
index 0000000..dc4b4a7
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+public class LogAbortException extends Exception {
+
+    private final String mFormat;
+    private final Object[] mArgs;
+
+    public LogAbortException(String format, Object... args) {
+        mFormat = format;
+        mArgs = args;
+    }
+    
+    public void error(Log log) {
+        log.error(mFormat, mArgs);
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
new file mode 100644
index 0000000..76bd8d4
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+public class Main {
+
+    public static void main(String[] args) {
+
+        Log log = new Log();
+
+        ArrayList<String> osJarPath = new ArrayList<String>();
+        String[] osDestJar = { null };
+
+        if (!processArgs(log, args, osJarPath, osDestJar)) {
+            log.error("Usage: layoutlib_create [-v] output.jar input.jar ...");
+            System.exit(1);
+        }
+
+        log.info("Output: %1$s", osDestJar[0]);
+        for (String path : osJarPath) {
+            log.info("Input :      %1$s", path);
+        }
+        
+        try {
+            AsmGenerator agen = new AsmGenerator(log, osDestJar[0],
+                    new Class<?>[] {  // classes to inject in the final JAR
+                        OverrideMethod.class,
+                        MethodListener.class,
+                        MethodAdapter.class
+                    },
+                    new String[] {  // methods to force override
+                        "android.view.View#isInEditMode",
+                        "android.content.res.Resources$Theme#obtainStyledAttributes",
+                    },
+                    new String[] {  // classes to rename (so that we can replace them in layoutlib)
+                        // original-platform-class-name ======> renamed-class-name
+                        "android.graphics.Matrix",              "android.graphics._Original_Matrix",
+                        "android.graphics.Paint",               "android.graphics._Original_Paint",
+                        "android.graphics.Typeface",            "android.graphics._Original_Typeface",
+                        "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
+                        "android.graphics.Path",                "android.graphics._Original_Path",
+                        "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",
+                        "android.graphics.Shader",              "android.graphics._Original_Shader",
+                        "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
+                        "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
+                        "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
+                        "android.graphics.RadialGradient",      "android.graphics._Original_RadialGradient",
+                        "android.graphics.SweepGradient",       "android.graphics._Original_SweepGradient",
+                        "android.util.FloatMath",               "android.util._Original_FloatMath",
+                        "android.view.SurfaceView",             "android.view._Original_SurfaceView",
+                    },
+                    new String[] { // methods deleted from their return type.
+                        "android.graphics.Paint", // class to delete method from
+                        "android.graphics.Paint$Align", // list of type identifying methods to delete
+                        "android.graphics.Paint$Style",
+                        "android.graphics.Paint$Join",
+                        "android.graphics.Paint$Cap",
+                        "android.graphics.Paint$FontMetrics",
+                        "android.graphics.Paint$FontMetricsInt",
+                        null }
+            );
+
+            AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
+                    new String[] { "android.view.View" },   // derived from
+                    new String[] {                          // include classes
+                        "android.*", // for android.R
+                        "android.util.*",
+                        "com.android.internal.util.*",
+                        "android.view.*",
+                        "android.widget.*",
+                        "com.android.internal.widget.*",
+                        "android.text.**",
+                        "android.graphics.*",
+                        "android.graphics.drawable.*",
+                        "android.content.*",
+                        "android.content.res.*",
+                        "org.apache.harmony.xml.*",
+                        "com.android.internal.R**",
+                        "android.pim.*", // for datepicker
+                        "android.os.*",  // for android.os.Handler
+                        });
+            aa.analyze();
+            agen.generate();
+            
+            // Throw an error if any class failed to get renamed by the generator
+            //
+            // IMPORTANT: if you're building the platform and you get this error message,
+            // it means the renameClasses[] array in AsmGenerator needs to be updated: some
+            // class should have been renamed but it was not found in the input JAR files.
+            Set<String> notRenamed = agen.getClassesNotRenamed();
+            if (notRenamed.size() > 0) {
+                // (80-column guide below for error formatting)
+                // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+                log.error(
+                  "ERROR when running layoutlib_create: the following classes are referenced\n" +
+                  "by tools/layoutlib/create but were not actually found in the input JAR files.\n" +
+                  "This may be due to some platform classes having been renamed.");
+                for (String fqcn : notRenamed) {
+                    log.error("- Class not found: %s", fqcn.replace('/', '.'));
+                }
+                for (String path : osJarPath) {
+                    log.info("- Input JAR : %1$s", path);
+                }
+                System.exit(1);
+            }
+            
+            System.exit(0);
+        } catch (IOException e) {
+            log.exception(e, "Failed to load jar");
+        } catch (LogAbortException e) {
+            e.error(log);
+        }
+
+        System.exit(1);
+    }
+
+    /**
+     * Returns true if args where properly parsed.
+     * Returns false if program should exit with command-line usage.
+     * <p/>
+     * Note: the String[0] is an output parameter wrapped in an array, since there is no
+     * "out" parameter support.
+     */
+    private static boolean processArgs(Log log, String[] args,
+            ArrayList<String> osJarPath, String[] osDestJar) {
+        for (int i = 0; i < args.length; i++) {
+            String s = args[i];
+            if (s.equals("-v")) {
+                log.setVerbose(true);
+            } else if (!s.startsWith("-")) {
+                if (osDestJar[0] == null) {
+                    osDestJar[0] = s;
+                } else {
+                    osJarPath.add(s);
+                }
+            } else {
+                log.error("Unknow argument: %s", s);
+                return false;
+            }
+        }
+        
+        if (osJarPath.isEmpty()) {
+            log.error("Missing parameter: path to input jar");
+            return false;
+        }
+        if (osDestJar[0] == null) {
+            log.error("Missing parameter: path to output jar");
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
new file mode 100644
index 0000000..627ea17
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+/**
+ * An adapter to make it easier to use {@link MethodListener}.
+ * <p/>
+ * The adapter calls the void {@link #onInvokeV(String, boolean, Object)} listener
+ * for all types (I, L, F, D and A), returning 0 or null as appropriate.
+ */
+public class MethodAdapter implements MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller) {
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return null;
+    }
+}
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
new file mode 100644
index 0000000..6fc2b24
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+/**
+ * Interface to allow a method invocation to be listened upon.
+ * <p/>
+ * This is used by {@link OverrideMethod} to register a listener for methods that
+ * have been stubbed by the {@link AsmGenerator}. At runtime the stub will call either a
+ * default global listener or a specific listener based on the method signature.
+ */
+public interface MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller);
+}
+    
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
new file mode 100644
index 0000000..a6aff99
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import java.util.HashMap;
+
+/**
+ * Allows stub methods from LayoutLib to be overriden at runtime.
+ * <p/>
+ * Implementation note: all types required by this class(inner/outer classes & interfaces)
+ * must be referenced by the injectClass argument to {@link AsmGenerator} in Main.java;
+ * Otherwise they won't be accessible in layoutlib.jar at runtime.
+ */
+public final class OverrideMethod {
+
+    /** Map of method overridden. */
+    private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
+    /** Default listener for all method not listed in sMethods. Nothing if null. */
+    private static MethodListener sDefaultListener = null;
+    
+    /**
+     * Sets the default listener for all methods not specifically handled.
+     * Null means to do nothing.
+     */
+    public static void setDefaultListener(MethodListener listener) {
+        sDefaultListener = listener;
+    }
+
+    /**
+     * Defines or reset a listener for the given method signature.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V"
+     * @param listener The new listener. Removes it if null.
+     */
+    public static void setMethodListener(String signature, MethodListener listener) {
+        if (listener == null) {
+            sMethods.remove(signature);
+        } else {
+            sMethods.put(signature, listener);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the given signature or the default one if defined.
+     * <p/>
+     * This version invokes the method listener for the void return type. 
+     * <p/>
+     * Note: this is not intended to be used by the LayoutLib Bridge. It is intended to be called
+     * by the stubbed methods generated by the LayoutLib_create tool.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public static void invokeV(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            i.onInvokeV(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            sDefaultListener.onInvokeV(signature, isNative, caller);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the int return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static int invokeI(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeI(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeI(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the long return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static long invokeL(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeL(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeL(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the float return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static float invokeF(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeF(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeF(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the double return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static double invokeD(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeD(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeD(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the object return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static Object invokeA(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeA(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeA(signature, isNative, caller);
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
new file mode 100644
index 0000000..0956b92
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+/**
+ * This class visitor renames a class from a given old name to a given new name.
+ * The class visitor will also rename all inner classes and references in the methods.
+ * <p/>
+ * 
+ * For inner classes, this handles only the case where the outer class name changes.
+ * The inner class name should remain the same. 
+ */
+public class RenameClassAdapter extends ClassAdapter {
+
+    
+    private final String mOldName;
+    private final String mNewName;
+    private String mOldBase;
+    private String mNewBase;
+
+    /**
+     * Creates a class visitor that renames a class from a given old name to a given new name.
+     * The class visitor will also rename all inner classes and references in the methods.
+     * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+     */
+    public RenameClassAdapter(ClassWriter cv, String oldName, String newName) {
+        super(cv);
+        mOldBase = mOldName = oldName;
+        mNewBase = mNewName = newName;
+        
+        int pos = mOldName.indexOf('$');
+        if (pos > 0) {
+            mOldBase = mOldName.substring(0, pos);
+        }
+        pos = mNewName.indexOf('$');
+        if (pos > 0) {
+            mNewBase = mNewName.substring(0, pos);
+        }
+        
+        assert (mOldBase == null && mNewBase == null) || (mOldBase != null && mNewBase != null);
+    }
+
+
+    /**
+     * Renames a type descriptor, e.g. "Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    String renameTypeDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        return renameType(Type.getType(desc));
+    }
+    
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    String renameType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            return "L" + renameInternalType(in) + ";";
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return sb.toString();
+        }
+        return type.getDescriptor();
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;".
+     * This is like renameType() except that it returns a Type object.
+     * If the type doesn't need to be renamed, returns the input type object.
+     */
+    Type renameTypeAsType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            String newIn = renameInternalType(in);
+            if (newIn != in) {
+                return Type.getType("L" + newIn + ";");
+            }
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return Type.getType(sb.toString());
+        }
+        return type;
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     * <p/>
+     * The internal type of some of the MethodVisitor turns out to be a type
+       descriptor sometimes so descriptors are renamed too.
+     */
+    String renameInternalType(String type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.equals(mOldName)) {
+            return mNewName;
+        }
+
+        if (mOldBase != mOldName && type.equals(mOldBase)) {
+            return mNewBase;
+        }
+    
+        int pos = type.indexOf('$');
+        if (pos == mOldBase.length() && type.startsWith(mOldBase)) {
+            return mNewBase + type.substring(pos);
+        }
+
+        // The internal type of some of the MethodVisitor turns out to be a type
+        // descriptor sometimes. This is the case with visitTypeInsn(type) and
+        // visitMethodInsn(owner). We try to detect it and adjust it here.
+        if (type.indexOf(';') > 0) {
+            type = renameTypeDesc(type);
+        }
+
+        return type;
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    String renameMethodDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        Type[] args = Type.getArgumentTypes(desc);
+
+        StringBuilder sb = new StringBuilder("(");
+        for (Type arg : args) {
+            String name = renameType(arg);
+            sb.append(name);
+        }
+        sb.append(')');
+        
+        Type ret = Type.getReturnType(desc);
+        String name = renameType(ret);
+        sb.append(name);
+
+        return sb.toString();
+    }
+
+    
+    /**
+     * Renames the ClassSignature handled by ClassVisitor.visit 
+     * or the MethodTypeSignature handled by ClassVisitor.visitMethod.
+     */
+    String renameTypeSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.accept(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+    
+    /**
+     * Renames the FieldTypeSignature handled by ClassVisitor.visitField
+     * or MethodVisitor.visitLocalVariable.
+     */
+    String renameFieldSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.acceptType(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+    
+    //----------------------------------
+    // Methods from the ClassAdapter
+    
+    @Override
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        name = renameInternalType(name);
+        superName = renameInternalType(superName);
+        signature = renameTypeSignature(signature);
+        
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        assert outerName.equals(mOldName);
+        outerName = renameInternalType(outerName);
+        name = outerName + "$" + innerName;
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        desc = renameMethodDesc(desc);
+        signature = renameTypeSignature(signature);
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        return new RenameMethodAdapter(mw);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        desc = renameTypeDesc(desc);
+        return super.visitAnnotation(desc, visible);
+    }
+    
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        desc = renameTypeDesc(desc);
+        signature = renameFieldSignature(signature);
+        return super.visitField(access, name, desc, signature, value);
+    }
+    
+    
+    //----------------------------------
+
+    /**
+     * A method visitor that renames all references from an old class name to a new class name.
+     */
+    public class RenameMethodAdapter extends MethodAdapter {
+
+        /**
+         * Creates a method visitor that renames all references from a given old name to a given new
+         * name. The method visitor will also rename all inner classes.
+         * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+         */
+        public RenameMethodAdapter(MethodVisitor mv) {
+            super(mv);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+            
+            return super.visitAnnotation(desc, visible);
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitParameterAnnotation(parameter, desc, visible);
+        }
+
+        @Override
+        public void visitTypeInsn(int opcode, String type) {
+            type = renameInternalType(type);
+            
+            super.visitTypeInsn(opcode, type);
+        }
+
+        @Override
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameTypeDesc(desc);
+
+            super.visitFieldInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameMethodDesc(desc);
+
+            super.visitMethodInsn(opcode, owner, name, desc);
+        }
+        
+        @Override
+        public void visitLdcInsn(Object cst) {
+            // If cst is a Type, this means the code is trying to pull the .class constant
+            // for this class, so it needs to be renamed too.
+            if (cst instanceof Type) {
+                cst = renameTypeAsType((Type) cst);
+            }
+            super.visitLdcInsn(cst);
+        }
+
+        @Override
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            desc = renameTypeDesc(desc);
+         
+            super.visitMultiANewArrayInsn(desc, dims);
+        }
+
+        @Override
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            type = renameInternalType(type);
+            
+            super.visitTryCatchBlock(start, end, handler, type);
+        }
+
+        @Override
+        public void visitLocalVariable(String name, String desc, String signature,
+                Label start, Label end, int index) {
+            desc = renameTypeDesc(desc);
+            signature = renameFieldSignature(signature);
+            
+            super.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+
+    }
+
+    //----------------------------------
+    
+    public class RenameSignatureAdapter implements SignatureVisitor {
+
+        private final SignatureVisitor mSv;
+
+        public RenameSignatureAdapter(SignatureVisitor sv) {
+            mSv = sv;
+        }
+
+        public void visitClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitClassType(name);
+        }
+
+        public void visitInnerClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitInnerClassType(name);
+        }
+
+        public SignatureVisitor visitArrayType() {
+            SignatureVisitor sv = mSv.visitArrayType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitBaseType(char descriptor) {
+            mSv.visitBaseType(descriptor);
+        }
+
+        public SignatureVisitor visitClassBound() {
+            SignatureVisitor sv = mSv.visitClassBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitEnd() {
+            mSv.visitEnd();
+        }
+
+        public SignatureVisitor visitExceptionType() {
+            SignatureVisitor sv = mSv.visitExceptionType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitFormalTypeParameter(String name) {
+            mSv.visitFormalTypeParameter(name);
+        }
+
+        public SignatureVisitor visitInterface() {
+            SignatureVisitor sv = mSv.visitInterface();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitInterfaceBound() {
+            SignatureVisitor sv = mSv.visitInterfaceBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitParameterType() {
+            SignatureVisitor sv = mSv.visitParameterType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitReturnType() {
+            SignatureVisitor sv = mSv.visitReturnType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitSuperclass() {
+            SignatureVisitor sv = mSv.visitSuperclass();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitTypeArgument() {
+            mSv.visitTypeArgument();
+        }
+
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            SignatureVisitor sv = mSv.visitTypeArgument(wildcard);
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitTypeVariable(String name) {
+            mSv.visitTypeVariable(name);
+        }
+        
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
new file mode 100644
index 0000000..9a57a4a
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This method adapter rewrites a method by discarding the original code and generating
+ * a stub depending on the return type. Original annotations are passed along unchanged.
+ */
+class StubMethodAdapter implements MethodVisitor {
+
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+    
+    /** The parent method writer */
+    private MethodVisitor mParentVisitor;
+    /** The method return type. Can be null. */
+    private Type mReturnType;
+    /** Message to be printed by stub methods. */
+    private String mInvokeSignature;
+    /** Flag to output the first line number. */
+    private boolean mOutputFirstLineNumber = true;
+    /** Flag that is true when implementing a constructor, to accept all original 
+     *  code calling the original super constructor. */
+    private boolean mIsInitMethod = false;
+
+    private boolean mMessageGenerated;
+    private final boolean mIsStatic;
+    private final boolean mIsNative;
+
+    public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
+            String invokeSignature, boolean isStatic, boolean isNative) {
+        mParentVisitor = mv;
+        mReturnType = returnType;
+        mInvokeSignature = invokeSignature;
+        mIsStatic = isStatic;
+        mIsNative = isNative;
+        
+        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
+            mIsInitMethod = true;
+        }
+    }
+    
+    private void generateInvoke() {
+        /* Generates the code:
+         *  OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
+         */
+        mParentVisitor.visitLdcInsn(mInvokeSignature);
+        // push true or false
+        mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+        // push null or this
+        if (mIsStatic) {
+            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+        }
+
+        int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
+        switch(sort) {
+        case Type.VOID:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeV",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
+            mParentVisitor.visitInsn(Opcodes.RETURN);
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeI",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
+            switch(sort) {
+            case Type.BOOLEAN:
+                Label l1 = new Label();
+                mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_1);
+                mParentVisitor.visitInsn(Opcodes.IRETURN);
+                mParentVisitor.visitLabel(l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_0);
+                break;
+            case Type.CHAR:
+                mParentVisitor.visitInsn(Opcodes.I2C);
+                break;
+            case Type.BYTE:
+                mParentVisitor.visitInsn(Opcodes.I2B);
+                break;
+            case Type.SHORT:
+                mParentVisitor.visitInsn(Opcodes.I2S);
+                break;
+            }
+            mParentVisitor.visitInsn(Opcodes.IRETURN);
+            break;
+        case Type.LONG:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeL",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
+            mParentVisitor.visitInsn(Opcodes.LRETURN);
+            break;
+        case Type.FLOAT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeF",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
+            mParentVisitor.visitInsn(Opcodes.FRETURN);
+            break;
+        case Type.DOUBLE:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeD",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
+            mParentVisitor.visitInsn(Opcodes.DRETURN);
+            break;
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeA",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
+            mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
+            mParentVisitor.visitInsn(Opcodes.ARETURN);
+            break;
+        }
+
+    }
+
+    private void generatePop() {
+        /* Pops the stack, depending on the return type.
+         */
+        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
+        case Type.VOID:
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+        case Type.FLOAT:
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitInsn(Opcodes.POP);
+            break;
+        case Type.LONG:
+        case Type.DOUBLE:
+            mParentVisitor.visitInsn(Opcodes.POP2);
+            break;
+        }
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    public void visitCode() {
+        mParentVisitor.visitCode();
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+        }
+        mParentVisitor.visitMaxs(maxStack, maxLocals);
+    }
+    
+    /**
+     * End of visiting.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    public void visitEnd() {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+            mParentVisitor.visitMaxs(1, 1);
+        }
+        mParentVisitor.visitEnd();
+    }
+
+    /* Writes all annotation from the original method. */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return mParentVisitor.visitAnnotation(desc, visible);
+    }
+
+    /* Writes all annotation default values from the original method. */
+    public AnnotationVisitor visitAnnotationDefault() {
+        return mParentVisitor.visitAnnotationDefault();
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    /* Writes all attributes from the original method. */
+    public void visitAttribute(Attribute attr) {
+        mParentVisitor.visitAttribute(attr);
+    }
+
+    /*
+     * Only writes the first line number present in the original code so that source
+     * viewers can direct to the correct method, even if the content doesn't match.
+     */
+    public void visitLineNumber(int line, Label start) {
+        if (mIsInitMethod || mOutputFirstLineNumber) {
+            mParentVisitor.visitLineNumber(line, start);
+            mOutputFirstLineNumber = false;
+        }
+    }
+
+    /**
+     * For non-constructor, rewrite existing "return" instructions to write the message.
+     */
+    public void visitInsn(int opcode) {
+        if (mIsInitMethod) {
+            switch (opcode) {
+            case Opcodes.RETURN:
+            case Opcodes.ARETURN:
+            case Opcodes.DRETURN:
+            case Opcodes.FRETURN:
+            case Opcodes.IRETURN:
+            case Opcodes.LRETURN:
+                // Pop the last word from the stack since invoke will generate its own return.
+                generatePop();
+                generateInvoke();
+                mMessageGenerated = true;
+            default:
+                mParentVisitor.visitInsn(opcode);
+            }
+        }
+    }
+
+    public void visitLabel(Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLabel(label);
+        }
+    }
+
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    public void visitIincInsn(int var, int increment) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIincInsn(var, increment);
+        }
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIntInsn(opcode, operand);
+        }
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitJumpInsn(opcode, label);
+        }
+    }
+
+    public void visitLdcInsn(Object cst) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLdcInsn(cst);
+        }
+    }
+
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    public void visitTypeInsn(int opcode, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTypeInsn(opcode, type);
+        }
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitVarInsn(opcode, var);
+        }
+    }
+    
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
new file mode 100644
index 0000000..e294d56
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Set;
+
+/**
+ * Class adapter that can stub some or all of the methods of the class. 
+ */
+class TransformClassAdapter extends ClassAdapter {
+
+    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
+    private final boolean mStubAll;
+    /** True if the class is an interface. */
+    private boolean mIsInterface;
+    private final String mClassName;
+    private final Log mLog;
+    private final Set<String> mStubMethods;
+    private Set<String> mDeleteReturns;
+
+    /**
+     * Creates a new class adapter that will stub some or all methods.
+     * @param logger 
+     * @param stubMethods 
+     * @param deleteReturns list of types that trigger the deletion of methods returning them.
+     * @param className The name of the class being modified
+     * @param cv The parent class writer visitor
+     * @param stubNativesOnly True if only native methods should be stubbed. False if all 
+     *                        methods should be stubbed.
+     * @param hasNative True if the method has natives, in which case its access should be
+     *                  changed.
+     */
+    public TransformClassAdapter(Log logger, Set<String> stubMethods,
+            Set<String> deleteReturns, String className, ClassVisitor cv,
+            boolean stubNativesOnly, boolean hasNative) {
+        super(cv);
+        mLog = logger;
+        mStubMethods = stubMethods;
+        mClassName = className;
+        mStubAll = !stubNativesOnly;
+        mIsInterface = false;
+        mDeleteReturns = deleteReturns;
+    }
+
+    /* Visits the class header. */
+    @Override
+    public void visit(int version, int access, String name,
+            String signature, String superName, String[] interfaces) {
+        
+        // This class might be being renamed.
+        name = mClassName;
+        
+        // remove protected or private and set as public
+        access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+        access |= Opcodes.ACC_PUBLIC;
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+    
+    /* Visits the header of an inner class. */
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        // remove protected or private and set as public
+        access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+        access |= Opcodes.ACC_PUBLIC;
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    /* Visits a method. */
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        
+        if (mDeleteReturns != null) {
+            Type t = Type.getReturnType(desc);
+            if (t.getSort() == Type.OBJECT) {
+                String returnType = t.getInternalName();
+                if (returnType != null) {
+                    if (mDeleteReturns.contains(returnType)) {
+                        return null;
+                    }
+                }
+            }
+        }
+
+        String methodSignature = mClassName.replace('/', '.') + "#" + name;
+
+        // change access to public
+        access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+        access |= Opcodes.ACC_PUBLIC;
+
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+
+        // stub this method if they are all to be stubbed or if it is a native method
+        // and don't try to stub interfaces nor abstract non-native methods.
+        if (!mIsInterface &&
+            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
+            (mStubAll ||
+             (access & Opcodes.ACC_NATIVE) != 0) ||
+             mStubMethods.contains(methodSignature)) {
+            
+            boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+            boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+            // remove abstract, final and native
+            access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
+            
+            String invokeSignature = methodSignature + desc;
+            mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
+            
+            MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+            return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
+                    isStatic, isNative);
+
+        } else {
+            mLog.debug("  Keep: %s %s", name, desc);
+            return super.visitMethod(access, name, desc, signature, exceptions);
+        }
+    }
+    
+    /* Visits a field. Makes it public. */
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        // change access to public
+        access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+        access |= Opcodes.ACC_PUBLIC;
+        
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /**
+     * Extracts the return {@link Type} of this descriptor.
+     */
+    Type returnType(String desc) {
+        if (desc != null) {
+            try {
+                return Type.getReturnType(desc);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // ignore, not a valid type.
+            }
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
new file mode 100644
index 0000000..603284e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.tools.layoutlib.create;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
+import com.android.tools.layoutlib.create.LogTest.MockLog;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Unit tests for some methods of {@link AsmAnalyzer}.
+ */
+public class AsmAnalyzerTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private AsmAnalyzer mAa;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new LogTest.MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+        
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */,
+                null /* deriveFrom */, null /* includeGlobs */ );
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testParseZip() throws IOException {
+        Map<String, ClassReader> map = mAa.parseZip(mOsJarPath);
+
+        assertArrayEquals(new String[] {
+                "mock_android.dummy.InnerTest",
+                "mock_android.dummy.InnerTest$DerivingClass",
+                "mock_android.dummy.InnerTest$MyGenerics1",
+                "mock_android.dummy.InnerTest$MyIntEnum",
+                "mock_android.dummy.InnerTest$MyStaticInnerClass",   
+                "mock_android.dummy.InnerTest$NotStaticInner1", 
+                "mock_android.dummy.InnerTest$NotStaticInner2",  
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.LinearLayout$LayoutParams",
+                "mock_android.widget.TableLayout",
+                "mock_android.widget.TableLayout$LayoutParams"
+            },
+            map.keySet().toArray());
+    }
+    
+    @Test
+    public void testFindClass() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
+                zipClasses, found);
+        
+        assertNotNull(cr);
+        assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName());
+        assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" },
+                found.keySet().toArray());
+        assertArrayEquals(new ClassReader[] { cr }, found.values().toArray());
+    }
+
+    @Test
+    public void testFindGlobs() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        // this matches classes, a package match returns nothing
+        found.clear();
+        mAa.findGlobs("mock_android.view", zipClasses, found);
+
+        assertArrayEquals(new String[] { },
+            found.keySet().toArray());
+
+        // a complex glob search. * is a search pattern that matches names, not dots
+        mAa.findGlobs("mock_android.*.*Group$*Layout*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // a complex glob search. ** is a search pattern that matches names including dots
+        mAa.findGlobs("mock_android.**Group*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // matches a single class
+        found.clear();
+        mAa.findGlobs("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View"
+            },
+            found.keySet().toArray());
+
+        // matches everyting inside the given package but not sub-packages
+        found.clear();
+        mAa.findGlobs("mock_android.view.*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        for (String key : found.keySet()) {
+            ClassReader value = found.get(key);
+            assertNotNull("No value for " + key, value);
+            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
+        }
+    }
+
+    @Test
+    public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.TableLayout",
+            },
+            found.keySet().toArray());
+        
+        for (String key : found.keySet()) {
+            ClassReader value = found.get(key);
+            assertNotNull("No value for " + key, value);
+            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
+        }
+    }
+    
+    @Test
+    public void testDependencyVisitor() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> out_deps = new TreeMap<String, ClassReader>();
+
+        ClassReader cr = mAa.findClass("mock_android.widget.TableLayout", zipClasses, keep);
+        DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
+        
+        // get first level dependencies
+        cr.accept(visitor, 0 /* flags */);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.TableLayout$LayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get second level dependencies
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get third level dependencies (there are none)
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
new file mode 100644
index 0000000..7cdf79a
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.tools.layoutlib.create;
+
+
+import static org.junit.Assert.assertArrayEquals;
+
+import com.android.tools.layoutlib.create.LogTest.MockLog;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Unit tests for some methods of {@link AsmGenerator}.
+ */
+public class AsmGeneratorTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private String mOsDestJar;
+    private File mTempFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new LogTest.MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+        
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mTempFile = File.createTempFile("mock", "jar");
+        mOsDestJar = mTempFile.getAbsolutePath();
+        mTempFile.deleteOnExit();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTempFile != null) {
+            mTempFile.delete();
+            mTempFile = null;
+        }
+    }
+
+    @Test
+    public void testClassRenaming() throws IOException, LogAbortException {
+        
+        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar,
+            null, // classes to inject in the final JAR
+            null,  // methods to force override
+            new String[] {  // classes to rename (so that we can replace them)
+                "mock_android.view.View", "mock_android.view._Original_View",
+                "not.an.actual.ClassName", "anoter.fake.NewClassName",
+            },
+            null // methods deleted from their return type.
+            );
+
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+                null,                 // derived from
+                new String[] {        // include classes
+                    "**"
+                });
+        aa.analyze();
+        agen.generate();
+        
+        Set<String> notRenamed = agen.getClassesNotRenamed();
+        assertArrayEquals(new String[] { "not/an/actual/ClassName" }, notRenamed.toArray());
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
new file mode 100644
index 0000000..3f13158
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LogTest {
+
+    public static class MockLog extends Log {
+        StringBuilder mOut = new StringBuilder();
+        StringBuilder mErr = new StringBuilder();
+        
+        public String getOut() {
+            return mOut.toString();
+        }
+        
+        public String getErr() {
+            return mErr.toString();
+        }
+        
+        @Override
+        protected void outPrintln(String msg) {
+            mOut.append(msg);
+            mOut.append('\n');
+        }
+        
+        @Override
+        protected void errPrintln(String msg) {
+            mErr.append(msg);
+            mErr.append('\n');
+        }
+    }
+
+    private MockLog mLog;
+    
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // pass
+    }
+
+    @Test
+    public void testDebug() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.setVerbose(false);
+        mLog.debug("Test %d", 42);
+        assertEquals("", mLog.getOut());
+
+        mLog.setVerbose(true);
+        mLog.debug("Test %d", 42);
+
+        assertEquals("Test 42\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testInfo() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.info("Test %d", 43);
+
+        assertEquals("Test 43\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testError() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.error("Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertEquals("Test 44\n", mLog.getErr());
+    }
+
+    @Test
+    public void testException() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        Exception e = new Exception("My Exception");
+        mLog.exception(e, "Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertTrue(mLog.getErr().startsWith("Test 44\njava.lang.Exception: My Exception"));
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
new file mode 100644
index 0000000..90c6a9c
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class RenameClassAdapterTest {
+
+    private RenameClassAdapter mOuter;
+    private RenameClassAdapter mInner;
+
+    @Before
+    public void setUp() throws Exception {
+        mOuter = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old",
+                                         "org.blah.New");
+        
+        mInner = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old$Inner",
+                                         "org.blah.New$Inner"); 
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    /**
+     * Renames a type, e.g. "Lcom.package.My;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameTypeDesc() {
+
+        // primitive types are left untouched
+        assertEquals("I", mOuter.renameTypeDesc("I"));
+        assertEquals("D", mOuter.renameTypeDesc("D"));
+        assertEquals("V", mOuter.renameTypeDesc("V"));
+
+        // object types that need no renaming are left untouched
+        assertEquals("Lcom.package.MyClass;", mOuter.renameTypeDesc("Lcom.package.MyClass;"));
+        assertEquals("Lcom.package.MyClass;", mInner.renameTypeDesc("Lcom.package.MyClass;"));
+
+        // object types that match the requirements
+        assertEquals("Lorg.blah.New;", mOuter.renameTypeDesc("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mInner.renameTypeDesc("Lcom.pack.Old$Inner;"));
+        // inner classes match the base type which is being renamed
+        assertEquals("Lorg.blah.New$Other;", mOuter.renameTypeDesc("Lcom.pack.Old$Other;"));
+        assertEquals("Lorg.blah.New$Other;", mInner.renameTypeDesc("Lcom.pack.Old$Other;"));
+
+        // arrays
+        assertEquals("[Lorg.blah.New;",  mOuter.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mOuter.renameTypeDesc("[[Lcom.pack.Old;"));
+        
+        assertEquals("[Lorg.blah.New;",  mInner.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mInner.renameTypeDesc("[[Lcom.pack.Old;"));
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    @Test
+    public void testRenameType() {
+        // Skip. This is actually tested by testRenameTypeDesc above.
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameInternalType() {
+        // a descriptor is not left untouched
+        assertEquals("Lorg.blah.New;", mOuter.renameInternalType("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mOuter.renameInternalType("Lcom.pack.Old$Inner;"));
+
+        // an actual FQCN
+        assertEquals("org.blah.New", mOuter.renameInternalType("com.pack.Old"));
+        assertEquals("org.blah.New$Inner", mOuter.renameInternalType("com.pack.Old$Inner"));
+
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    @Test
+    public void testRenameMethodDesc() {
+        assertEquals("(IDLorg.blah.New;[Lorg.blah.New$Inner;)Lorg.blah.New$Other;",
+               mOuter.renameMethodDesc("(IDLcom.pack.Old;[Lcom.pack.Old$Inner;)Lcom.pack.Old$Other;"));
+    }
+
+    
+
+}
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
new file mode 100644
index 0000000..a7ea74f
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/data/mock_android.jardesc b/tools/layoutlib/create/tests/data/mock_android.jardesc
new file mode 100644
index 0000000..95f7591
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jardesc
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="C:/ralf/google/src/raphael-lapdroid/device/tools/layoutlib/create/tests/data/mock_android.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/layoutlib_create/tests/data/mock_android.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.widget"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.view"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.dummy"/>
+    </selectedElements>
+</jardesc>
diff --git a/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
new file mode 100644
index 0000000..e355ead
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.dummy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class InnerTest {
+
+    private int mSomeField;
+    private MyStaticInnerClass mInnerInstance;
+    private MyIntEnum mTheIntEnum;
+    private MyGenerics1<int[][], InnerTest, MyIntEnum, float[]> mGeneric1;
+
+    public class NotStaticInner2 extends NotStaticInner1 {
+
+    }
+
+    public class NotStaticInner1 {
+
+        public void someThing() {
+            mSomeField = 2;
+            mInnerInstance = null;
+        }
+
+    }
+
+    private static class MyStaticInnerClass {
+
+    }
+    
+    private static class DerivingClass extends InnerTest {
+        
+    }
+    
+    // enums are a kind of inner static class
+    public enum MyIntEnum {
+        VALUE0(0),
+        VALUE1(1),
+        VALUE2(2);
+
+        MyIntEnum(int myInt) {
+            this.myInt = myInt;
+        }
+        final int myInt;
+    }
+    
+    public static class MyGenerics1<T, U, V, W> {
+        public MyGenerics1() {
+            int a = 1;
+        }
+    }
+    
+    public <X> void genericMethod1(X a, X[] a) {
+    }
+
+    public <X, Y> void genericMethod2(X a, List<Y> b) {
+    }
+
+    public <X, Y> void genericMethod3(X a, List<Y extends InnerTest> b) {
+    }
+
+    public <T extends InnerTest> void genericMethod4(T[] a, Collection<T> b, Collection<?> c) {
+        Iterator<T> i = b.iterator();
+    }
+
+    public void someMethod(InnerTest self) {
+        mSomeField = self.mSomeField;
+        MyStaticInnerClass m = new MyStaticInnerClass();
+        mInnerInstance = m;
+        mTheIntEnum = null;
+        mGeneric1 = new MyGenerics1();
+        genericMethod(new DerivingClass[0], new ArrayList<DerivingClass>(), new ArrayList<InnerTest>());
+    }
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/mock_android/view/View.java
new file mode 100644
index 0000000..a80a98d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/View.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.view;
+
+public class View {
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
new file mode 100644
index 0000000..466470f
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.view;
+
+public class ViewGroup extends View {
+
+    public class MarginLayoutParams extends LayoutParams {
+
+    }
+
+    public class LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
new file mode 100644
index 0000000..3870a63
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class LinearLayout extends ViewGroup {
+
+    public class LayoutParams extends mock_android.view.ViewGroup.LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
new file mode 100644
index 0000000..e455e7d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class TableLayout extends ViewGroup {
+
+    public class LayoutParams extends MarginLayoutParams {
+
+    }
+
+}
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
new file mode 100644
index 0000000..186177f
--- /dev/null
+++ b/tools/localize/Android.mk
@@ -0,0 +1,56 @@
+# 
+# Copyright 2006 The Android Open Source Project
+#
+# Android Asset Packaging Tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    file_utils.cpp \
+    localize.cpp \
+    merge_res_and_xliff.cpp \
+    res_check.cpp \
+    xmb.cpp \
+    Configuration.cpp \
+    Perforce.cpp \
+    SourcePos.cpp \
+    Values.cpp \
+    ValuesFile.cpp \
+    XLIFFFile.cpp \
+    XMLHandler.cpp
+
+LOCAL_C_INCLUDES := \
+    external/expat/lib \
+    build/libs/host/include
+
+LOCAL_CFLAGS += -g -O0
+
+LOCAL_STATIC_LIBRARIES := \
+    libexpat \
+    libhost \
+    libutils \
+	libcutils
+    
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lrt
+endif
+
+
+LOCAL_MODULE := localize
+
+ifeq (a,a)
+    LOCAL_CFLAGS += -DLOCALIZE_WITH_TESTS
+    LOCAL_SRC_FILES += \
+        test.cpp \
+        localize_test.cpp \
+        merge_res_and_xliff_test.cpp \
+        Perforce_test.cpp \
+        ValuesFile_test.cpp \
+        XLIFFFile_test.cpp \
+        XMLHandler_test.cpp
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/localize/Configuration.cpp b/tools/localize/Configuration.cpp
new file mode 100644
index 0000000..56addbd
--- /dev/null
+++ b/tools/localize/Configuration.cpp
@@ -0,0 +1,76 @@
+#include "Configuration.h"
+#include <string.h>
+
+int
+Configuration::Compare(const Configuration& that) const
+{
+    int n;
+
+    n = locale.compare(that.locale);
+    if (n != 0) return n;
+
+    n = vendor.compare(that.vendor);
+    if (n != 0) return n;
+
+    n = orientation.compare(that.orientation);
+    if (n != 0) return n;
+
+    n = density.compare(that.density);
+    if (n != 0) return n;
+
+    n = touchscreen.compare(that.touchscreen);
+    if (n != 0) return n;
+
+    n = keyboard.compare(that.keyboard);
+    if (n != 0) return n;
+
+    n = navigation.compare(that.navigation);
+    if (n != 0) return n;
+
+    n = screenSize.compare(that.screenSize);
+    if (n != 0) return n;
+
+    return 0;
+}
+
+string
+Configuration::ToString() const
+{
+    string s;
+    if (locale.length() > 0) {
+        if (s.length() > 0) {
+            s += "-";
+        }
+        s += locale;
+    }
+    return s;
+}
+
+bool
+split_locale(const string& in, string* language, string* region)
+{
+    const int len = in.length();
+    if (len == 2) {
+        if (isalpha(in[0]) && isalpha(in[1])) {
+            *language = in;
+            region->clear();
+            return true;
+        } else {
+            return false;
+        }
+    }
+    else if (len == 5) {
+        if (isalpha(in[0]) && isalpha(in[1]) && (in[2] == '_' || in[2] == '-')
+                && isalpha(in[3]) && isalpha(in[4])) {
+            language->assign(in.c_str(), 2);
+            region->assign(in.c_str()+3, 2);
+            return true;
+        } else {
+            return false;
+        }
+    }
+    else {
+        return false;
+    }
+}
+
diff --git a/tools/localize/Configuration.h b/tools/localize/Configuration.h
new file mode 100644
index 0000000..f91bf04
--- /dev/null
+++ b/tools/localize/Configuration.h
@@ -0,0 +1,38 @@
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+#include <string>
+
+using namespace std;
+
+struct Configuration
+{
+    string locale;
+    string vendor;
+    string orientation;
+    string density;
+    string touchscreen;
+    string keyboard;
+    string navigation;
+    string screenSize;
+
+    // Compare two configurations
+    int Compare(const Configuration& that) const;
+
+    inline bool operator<(const Configuration& that) const { return Compare(that) < 0; }
+    inline bool operator<=(const Configuration& that) const { return Compare(that) <= 0; }
+    inline bool operator==(const Configuration& that) const { return Compare(that) == 0; }
+    inline bool operator!=(const Configuration& that) const { return Compare(that) != 0; }
+    inline bool operator>=(const Configuration& that) const { return Compare(that) >= 0; }
+    inline bool operator>(const Configuration& that) const { return Compare(that) > 0; }
+
+    // Parse a directory name, like "values-en-rUS".  Return the first segment in resType.
+    bool ParseDiectoryName(const string& dir, string* resType);
+
+    string ToString() const;
+};
+
+bool split_locale(const string& in, string* language, string* region);
+
+
+#endif // CONFIGURATION_H
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
new file mode 100644
index 0000000..3425668
--- /dev/null
+++ b/tools/localize/Perforce.cpp
@@ -0,0 +1,230 @@
+#include "Perforce.h"
+#include "log.h"
+#include <string.h>
+#include <stdlib.h>
+#include <sstream>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+using namespace std;
+
+extern char** environ;
+
+int
+Perforce::RunCommand(const string& cmd, string* result, bool printOnFailure)
+{
+    int err;
+    int outPipe[2];
+    int errPipe[2];
+    pid_t pid;
+
+    log_printf("Perforce::RunCommand: %s\n", cmd.c_str());
+
+    err = pipe(outPipe);
+    err |= pipe(errPipe);
+    if (err == -1) {
+        printf("couldn't create pipe. exiting.\n");
+        exit(1);
+        return -1;
+    }
+
+    pid = fork();
+    if (pid == -1) {
+        printf("couldn't fork. eixiting\n");
+        exit(1);
+        return -1;
+    }
+    else if (pid == 0) {
+        char const* args[] = {
+            "/bin/sh",
+            "-c",
+            cmd.c_str(),
+            NULL
+        };
+        close(outPipe[0]);
+        close(errPipe[0]);
+        dup2(outPipe[1], 1);
+        dup2(errPipe[1], 2);
+        execve(args[0], (char* const*)args, environ);
+        // done
+    }
+
+    close(outPipe[1]);
+    close(errPipe[1]);
+
+    result->clear();
+
+    char buf[1024];
+
+    // stdout
+    while (true) {
+        size_t amt = read(outPipe[0], buf, sizeof(buf));
+        result->append(buf, amt);
+        if (amt <= 0) {
+            break;
+        }
+    }
+
+    // stderr -- the messages are short so it ought to just fit in the buffer
+    string error;
+    while (true) {
+        size_t amt = read(errPipe[0], buf, sizeof(buf));
+        error.append(buf, amt);
+        if (amt <= 0) {
+            break;
+        }
+    }
+
+    close(outPipe[0]);
+    close(errPipe[0]);
+
+    waitpid(pid, &err, 0);
+    if (WIFEXITED(err)) {
+        err = WEXITSTATUS(err);
+    } else {
+        err = -1;
+    }
+    if (err != 0 && printOnFailure) {
+        write(2, error.c_str(), error.length());
+    }
+    return err;
+}
+
+int
+Perforce::GetResourceFileNames(const string& version, const string& base,
+                                const vector<string>& apps, vector<string>* results,
+                                bool printOnFailure)
+{
+    int err;
+    string text;
+    stringstream cmd;
+
+    cmd << "p4 files";
+
+    const size_t I = apps.size();
+    for (size_t i=0; i<I; i++) {
+        cmd << " \"" << base << '/' << apps[i] << "/res/values/strings.xml@" << version << '"';
+    }
+
+    err = RunCommand(cmd.str(), &text, printOnFailure);
+
+    const char* str = text.c_str();
+    while (*str) {
+        const char* lineend = strchr(str, '\n');
+        if (lineend == str) {
+            str++;
+            continue;
+        }
+        if (lineend-str > 1023) {
+            fprintf(stderr, "line too long!\n");
+            return 1;
+        }
+
+        string s(str, lineend-str);
+
+        char filename[1024];
+        char edit[1024];
+        int count = sscanf(str, "%[^#]#%*d - %s change %*d %*[^\n]\n", filename, edit);
+
+        if (count == 2 && 0 != strcmp("delete", edit)) {
+            results->push_back(string(filename));
+        }
+
+        str = lineend + 1;
+    }
+
+    return err;
+}
+
+int
+Perforce::GetFile(const string& file, const string& version, string* result,
+        bool printOnFailure)
+{
+    stringstream cmd;
+    cmd << "p4 print -q \"" << file << '@' << version << '"';
+    return RunCommand(cmd.str(), result, printOnFailure);
+}
+
+string
+Perforce::GetCurrentChange(bool printOnFailure)
+{
+    int err;
+    string text;
+
+    err = RunCommand("p4 changes -m 1 \\#have", &text, printOnFailure);
+    if (err != 0) {
+        return "";
+    }
+
+    long long n;
+    int count = sscanf(text.c_str(), "Change %lld on", &n);
+    if (count != 1) {
+        return "";
+    }
+
+    char result[100];
+    sprintf(result, "%lld", n);
+
+    return string(result);
+}
+
+static int
+do_files(const string& op, const vector<string>& files, bool printOnFailure)
+{
+    string text;
+    stringstream cmd;
+
+    cmd << "p4 " << op;
+
+    const size_t I = files.size();
+    for (size_t i=0; i<I; i++) {
+        cmd << " \"" << files[i] << "\"";
+    }
+
+    return Perforce::RunCommand(cmd.str(), &text, printOnFailure);
+}
+
+int
+Perforce::EditFiles(const vector<string>& files, bool printOnFailure)
+{
+    return do_files("edit", files, printOnFailure);
+}
+
+int
+Perforce::AddFiles(const vector<string>& files, bool printOnFailure)
+{
+    return do_files("add", files, printOnFailure);
+}
+
+int
+Perforce::DeleteFiles(const vector<string>& files, bool printOnFailure)
+{
+    return do_files("delete", files, printOnFailure);
+}
+
+string
+Perforce::Where(const string& depotPath, bool printOnFailure)
+{
+    int err;
+    string text;
+    string cmd = "p4 where ";
+    cmd += depotPath;
+
+    err = RunCommand(cmd, &text, printOnFailure);
+    if (err != 0) {
+        return "";
+    }
+
+    size_t index = text.find(' ');
+    if (index == text.npos) {
+        return "";
+    }
+    index = text.find(' ', index+1)+1;
+    if (index == text.npos) {
+        return "";
+    }
+
+    return text.substr(index, text.length()-index-1);
+}
+
diff --git a/tools/localize/Perforce.h b/tools/localize/Perforce.h
new file mode 100644
index 0000000..522797d
--- /dev/null
+++ b/tools/localize/Perforce.h
@@ -0,0 +1,25 @@
+#ifndef PERFORCE_H
+#define PERFORCE_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+class Perforce
+{
+public:
+    static int RunCommand(const string& cmd, string* result, bool printOnFailure);
+    static int GetResourceFileNames(const string& version, const string& base,
+                                const vector<string>& apps, vector<string>* result,
+                                bool printOnFailure);
+    static int GetFile(const string& file, const string& version, string* result,
+                                bool printOnFailure);
+    static string GetCurrentChange(bool printOnFailure);
+    static int EditFiles(const vector<string>& filename, bool printOnFailure);
+    static int AddFiles(const vector<string>& files, bool printOnFailure);
+    static int DeleteFiles(const vector<string>& files, bool printOnFailure);
+    static string Where(const string& depotPath, bool printOnFailure);
+};
+
+#endif // PERFORCE_H
diff --git a/tools/localize/Perforce_test.cpp b/tools/localize/Perforce_test.cpp
new file mode 100644
index 0000000..142b20e
--- /dev/null
+++ b/tools/localize/Perforce_test.cpp
@@ -0,0 +1,62 @@
+#include "Perforce.h"
+#include <stdio.h>
+
+static int
+RunCommand_test()
+{
+    string result;
+    int err = Perforce::RunCommand("p4 help csommands", &result, true);
+    printf("err=%d result=[[%s]]\n", err, result.c_str());
+    return 0;
+}
+
+static int
+GetResourceFileNames_test()
+{
+    vector<string> results;
+    vector<string> apps;
+    apps.push_back("apps/common");
+    apps.push_back("apps/Contacts");
+    int err = Perforce::GetResourceFileNames("43019", "//device", apps, &results, true);
+    if (err != 0) {
+        return err;
+    }
+    if (results.size() != 2) {
+        return 1;
+    }
+    if (results[0] != "//device/apps/common/res/values/strings.xml") {
+        return 1;
+    }
+    if (results[1] != "//device/apps/Contacts/res/values/strings.xml") {
+        return 1;
+    }
+    if (false) {
+        for (size_t i=0; i<results.size(); i++) {
+            printf("[%zd] '%s'\n", i, results[i].c_str());
+        }
+    }
+    return 0;
+}
+
+static int
+GetFile_test()
+{
+    string result;
+    int err = Perforce::GetFile("//device/Makefile", "296", &result, true);
+    printf("err=%d result=[[%s]]\n", err, result.c_str());
+    return 0;
+}
+
+int
+Perforce_test()
+{
+    bool all = false;
+    int err = 0;
+
+    if (all) err |= RunCommand_test();
+    if (all) err |= GetResourceFileNames_test();
+    if (all) err |= GetFile_test();
+
+    return err;
+}
+
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
new file mode 100644
index 0000000..9d7c5c6
--- /dev/null
+++ b/tools/localize/SourcePos.cpp
@@ -0,0 +1,166 @@
+#include "SourcePos.h"
+
+#include <stdarg.h>
+#include <set>
+
+using namespace std;
+
+const SourcePos GENERATED_POS("<generated>", -1);
+
+// ErrorPos
+// =============================================================================
+struct ErrorPos
+{
+    string file;
+    int line;
+    string error;
+
+    ErrorPos();
+    ErrorPos(const ErrorPos& that);
+    ErrorPos(const string& file, int line, const string& error);
+    ~ErrorPos();
+    bool operator<(const ErrorPos& rhs) const;
+    bool operator==(const ErrorPos& rhs) const;
+    ErrorPos& operator=(const ErrorPos& rhs);
+
+    void Print(FILE* to) const;
+};
+
+static set<ErrorPos> g_errors;
+
+ErrorPos::ErrorPos()
+{
+}
+
+ErrorPos::ErrorPos(const ErrorPos& that)
+    :file(that.file),
+     line(that.line),
+     error(that.error)
+{
+}
+
+ErrorPos::ErrorPos(const string& f, int l, const string& e)
+    :file(f),
+     line(l),
+     error(e)
+{
+}
+
+ErrorPos::~ErrorPos()
+{
+}
+
+bool
+ErrorPos::operator<(const ErrorPos& rhs) const
+{
+    if (this->file < rhs.file) return true;
+    if (this->file == rhs.file) {
+        if (this->line < rhs.line) return true;
+        if (this->line == rhs.line) {
+            if (this->error < rhs.error) return true;
+        }
+    }
+    return false;
+}
+
+bool
+ErrorPos::operator==(const ErrorPos& rhs) const
+{
+    return this->file == rhs.file
+            && this->line == rhs.line
+            && this->error == rhs.error;
+}
+
+ErrorPos&
+ErrorPos::operator=(const ErrorPos& rhs)
+{
+    this->file = rhs.file;
+    this->line = rhs.line;
+    this->error = rhs.error;
+    return *this;
+}
+
+void
+ErrorPos::Print(FILE* to) const
+{
+    if (this->line >= 0) {
+        fprintf(to, "%s:%d: %s\n", this->file.c_str(), this->line, this->error.c_str());
+    } else {
+        fprintf(to, "%s: %s\n", this->file.c_str(), this->error.c_str());
+    }
+}
+
+// SourcePos
+// =============================================================================
+SourcePos::SourcePos(const string& f, int l)
+    : file(f), line(l)
+{
+}
+
+SourcePos::SourcePos(const SourcePos& that)
+    : file(that.file), line(that.line)
+{
+}
+
+SourcePos::SourcePos()
+    : file("???", 0)
+{
+}
+
+SourcePos::~SourcePos()
+{
+}
+
+string
+SourcePos::ToString() const
+{
+    char buf[1024];
+    if (this->line >= 0) {
+        snprintf(buf, sizeof(buf)-1, "%s:%d", this->file.c_str(), this->line);
+    } else {
+        snprintf(buf, sizeof(buf)-1, "%s:", this->file.c_str());
+    }
+    buf[sizeof(buf)-1] = '\0';
+    return string(buf);
+}
+
+int
+SourcePos::Error(const char* fmt, ...) const
+{
+    int retval=0;
+    char buf[1024];
+    va_list ap;
+    va_start(ap, fmt);
+    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    char* p = buf + retval - 1;
+    while (p > buf && *p == '\n') {
+        *p = '\0';
+        p--;
+    }
+    ErrorPos err(this->file, this->line, string(buf));
+    if (g_errors.find(err) == g_errors.end()) {
+        err.Print(stderr);
+        g_errors.insert(err);
+    }
+    return retval;
+}
+
+bool
+SourcePos::HasErrors()
+{
+    return g_errors.size() > 0;
+}
+
+void
+SourcePos::PrintErrors(FILE* to)
+{
+    set<ErrorPos>::const_iterator it;
+    for (it=g_errors.begin(); it!=g_errors.end(); it++) {
+        it->Print(to);
+    }
+}
+
+
+
+
diff --git a/tools/localize/SourcePos.h b/tools/localize/SourcePos.h
new file mode 100644
index 0000000..5027129
--- /dev/null
+++ b/tools/localize/SourcePos.h
@@ -0,0 +1,28 @@
+#ifndef SOURCEPOS_H
+#define SOURCEPOS_H
+
+#include <string>
+
+using namespace std;
+
+class SourcePos
+{
+public:
+    string file;
+    int line;
+
+    SourcePos(const string& f, int l);
+    SourcePos(const SourcePos& that);
+    SourcePos();
+    ~SourcePos();
+
+    string ToString() const;
+    int Error(const char* fmt, ...) const;
+
+    static bool HasErrors();
+    static void PrintErrors(FILE* to);
+};
+
+extern const SourcePos GENERATED_POS;
+
+#endif // SOURCEPOS_H
diff --git a/tools/localize/Values.cpp b/tools/localize/Values.cpp
new file mode 100644
index 0000000..e396f8b
--- /dev/null
+++ b/tools/localize/Values.cpp
@@ -0,0 +1,134 @@
+#include "Values.h"
+#include <stdlib.h>
+
+
+// =====================================================================================
+StringResource::StringResource(const SourcePos& p, const string& f, const Configuration& c, 
+                    const string& i, int ix, XMLNode* v, const int ve, const string& vs,
+                    const string& cmnt)
+    :pos(p),
+     file(f),
+     config(c),
+     id(i),
+     index(ix),
+     value(v),
+     version(ve),
+     versionString(vs),
+     comment(cmnt)
+{
+}
+
+StringResource::StringResource()
+    :pos(),
+     file(),
+     config(),
+     id(),
+     index(-1),
+     value(NULL),
+     version(),
+     versionString(),
+     comment()
+{
+}
+
+StringResource::StringResource(const StringResource& that)
+    :pos(that.pos),
+     file(that.file),
+     config(that.config),
+     id(that.id),
+     index(that.index),
+     value(that.value),
+     version(that.version),
+     versionString(that.versionString),
+     comment(that.comment)
+{
+}
+
+int
+StringResource::Compare(const StringResource& that) const
+{
+    if (file != that.file) {
+        return file < that.file ? -1 : 1;
+    }
+    if (id != that.id) {
+        return id < that.id ? -1 : 1;
+    }
+    if (index != that.index) {
+        return index - that.index;
+    }
+    if (config != that.config) {
+        return config < that.config ? -1 : 1;
+    }
+    if (version != that.version) {
+        return version < that.version ? -1 : 1;
+    }
+    return 0;
+}
+
+string
+StringResource::TypedID() const
+{
+    string result;
+    if (index < 0) {
+        result = "string:";
+    } else {
+        char n[20];
+        sprintf(n, "%d:", index);
+        result = "array:";
+        result += n;
+    }
+    result += id;
+    return result;
+}
+
+static void
+split(const string& raw, vector<string>*parts)
+{
+    size_t index = 0;
+    while (true) {
+        size_t next = raw.find(':', index);
+        if (next != raw.npos) {
+            parts->push_back(string(raw, index, next-index));
+            index = next + 1;
+        } else {
+            parts->push_back(string(raw, index));
+            break;
+        }
+    }
+}
+
+bool
+StringResource::ParseTypedID(const string& raw, string* id, int* index)
+{
+    vector<string> parts;
+    split(raw, &parts);
+
+    const size_t N = parts.size();
+
+    for (size_t i=0; i<N; i++) {
+        if (parts[i].length() == 0) {
+            return false;
+        }
+    }
+
+    if (N == 2 && parts[0] == "string") {
+        *id = parts[1];
+        *index = -1;
+        return true;
+    }
+    else if (N == 3 && parts[0] == "array") {
+        char* p;
+        int n = (int)strtol(parts[1].c_str(), &p, 0);
+        if (*p == '\0') {
+            *id = parts[2];
+            *index = n;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    else {
+        return false;
+    }
+}
+
diff --git a/tools/localize/Values.h b/tools/localize/Values.h
new file mode 100644
index 0000000..0a60b6d
--- /dev/null
+++ b/tools/localize/Values.h
@@ -0,0 +1,48 @@
+#ifndef VALUES_H
+#define VALUES_H
+
+#include "Configuration.h"
+#include "XMLHandler.h"
+
+#include <string>
+
+using namespace std;
+
+enum {
+    CURRENT_VERSION,
+    OLD_VERSION
+};
+
+struct StringResource
+{
+    StringResource();
+    StringResource(const SourcePos& pos, const string& file, const Configuration& config, 
+                    const string& id, int index, XMLNode* value,
+                    int version, const string& versionString, const string& comment = "");
+    StringResource(const StringResource& that);
+
+    // Compare two configurations
+    int Compare(const StringResource& that) const;
+
+    inline bool operator<(const StringResource& that) const { return Compare(that) < 0; }
+    inline bool operator<=(const StringResource& that) const { return Compare(that) <= 0; }
+    inline bool operator==(const StringResource& that) const { return Compare(that) == 0; }
+    inline bool operator!=(const StringResource& that) const { return Compare(that) != 0; }
+    inline bool operator>=(const StringResource& that) const { return Compare(that) >= 0; }
+    inline bool operator>(const StringResource& that) const { return Compare(that) > 0; }
+
+    string TypedID() const;
+    static bool ParseTypedID(const string& typed, string* id, int* index);
+
+    SourcePos pos;
+    string file;
+    Configuration config;
+    string id;
+    int index;
+    XMLNode* value;
+    int version;
+    string versionString;
+    string comment;
+};
+
+#endif // VALUES_H
diff --git a/tools/localize/ValuesFile.cpp b/tools/localize/ValuesFile.cpp
new file mode 100644
index 0000000..bd6f494
--- /dev/null
+++ b/tools/localize/ValuesFile.cpp
@@ -0,0 +1,266 @@
+#include "ValuesFile.h"
+
+#include "XMLHandler.h"
+
+#include <algorithm>
+#include <fcntl.h>
+#include <expat.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace std;
+
+const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android";
+const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
+
+const char *const NS_MAP[] = {
+    "android", ANDROID_XMLNS,
+    "xliff", XLIFF_XMLNS,
+    NULL, NULL
+};
+
+const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP);
+
+
+// =====================================================================================
+class ArrayHandler : public XMLHandler
+{
+public:
+    ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id);
+
+    virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next);
+    virtual int OnText(const SourcePos& pos, const string& text);
+    virtual int OnComment(const SourcePos& pos, const string& text);
+
+private:
+    ValuesFile* m_vf;
+    int m_version;
+    int m_index;
+    string m_versionString;
+    string m_id;
+    string m_comment;
+};
+
+ArrayHandler::ArrayHandler(ValuesFile* vf, int version, const string& versionString,
+                            const string& id)
+    :m_vf(vf),
+     m_version(version),
+     m_index(0),
+     m_versionString(versionString),
+     m_id(id)
+{
+}
+
+int
+ArrayHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+    if (ns == "" && name == "item") {
+        XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
+        m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
+                                            m_id, m_index, node, m_version, m_versionString,
+                                            trim_string(m_comment)));
+        *next = new NodeHandler(node, XMLNode::EXACT);
+        m_index++;
+        m_comment = "";
+        return 0;
+    } else {
+        pos.Error("invalid <%s> element inside <array>\n", name.c_str());
+        return 1;
+    }
+}
+
+int
+ArrayHandler::OnText(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+ArrayHandler::OnComment(const SourcePos& pos, const string& text)
+{
+    m_comment += text;
+    return 0;
+}
+
+// =====================================================================================
+class ValuesHandler : public XMLHandler
+{
+public:
+    ValuesHandler(ValuesFile* vf, int version, const string& versionString);
+
+    virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next);
+    virtual int OnText(const SourcePos& pos, const string& text);
+    virtual int OnComment(const SourcePos& pos, const string& text);
+
+private:
+    ValuesFile* m_vf;
+    int m_version;
+    string m_versionString;
+    string m_comment;
+};
+
+ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString)
+    :m_vf(vf),
+     m_version(version),
+     m_versionString(versionString)
+{
+}
+
+int
+ValuesHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+    if (ns == "" && name == "string") {
+        string id = XMLAttribute::Find(attrs, "", "name", "");
+        XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
+        m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
+                                            id, -1, node, m_version, m_versionString,
+                                            trim_string(m_comment)));
+        *next = new NodeHandler(node, XMLNode::EXACT);
+    }
+    else if (ns == "" && name == "array") {
+        string id = XMLAttribute::Find(attrs, "", "name", "");
+        *next = new ArrayHandler(m_vf, m_version, m_versionString, id);
+    }
+    m_comment = "";
+    return 0;
+}
+
+int
+ValuesHandler::OnText(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+ValuesHandler::OnComment(const SourcePos& pos, const string& text)
+{
+    m_comment += text;
+    return 0;
+}
+
+// =====================================================================================
+ValuesFile::ValuesFile(const Configuration& config)
+    :m_config(config),
+     m_strings(),
+     m_arrays()
+{
+}
+
+ValuesFile::~ValuesFile()
+{
+}
+
+ValuesFile*
+ValuesFile::ParseFile(const string& filename, const Configuration& config,
+                    int version, const string& versionString)
+{
+    ValuesFile* result = new ValuesFile(config);
+
+    TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
+    XMLHandler::ParseFile(filename, &top);
+
+    return result;
+}
+
+ValuesFile*
+ValuesFile::ParseString(const string& filename, const string& text, const Configuration& config,
+                    int version, const string& versionString)
+{
+    ValuesFile* result = new ValuesFile(config);
+
+    TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
+    XMLHandler::ParseString(filename, text, &top);
+
+    return result;
+}
+
+const Configuration&
+ValuesFile::GetConfiguration() const
+{
+    return m_config;
+}
+
+void
+ValuesFile::AddString(const StringResource& str)
+{
+    if (str.index < 0) {
+        m_strings.insert(str);
+    } else {
+        m_arrays[str.id].insert(str);
+    }
+}
+
+set<StringResource>
+ValuesFile::GetStrings() const
+{
+    set<StringResource> result = m_strings;
+
+    for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
+            it != m_arrays.end(); it++) {
+        result.insert(it->second.begin(), it->second.end());
+    }
+
+    return result;
+}
+
+XMLNode*
+ValuesFile::ToXMLNode() const
+{
+    XMLNode* root;
+
+    // <resources>
+    {
+        vector<XMLAttribute> attrs;
+        ANDROID_NAMESPACES.AddToAttributes(&attrs);
+        root = XMLNode::NewElement(GENERATED_POS, "", "resources", attrs, XMLNode::PRETTY);
+    }
+
+    // <array>
+    for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
+            it != m_arrays.end(); it++) {
+        vector<XMLAttribute> arrayAttrs;
+        arrayAttrs.push_back(XMLAttribute("", "name", it->first));
+        const set<StringResource>& items = it->second;
+        XMLNode* arrayNode = XMLNode::NewElement(items.begin()->pos, "", "array", arrayAttrs,
+                XMLNode::PRETTY);
+        root->EditChildren().push_back(arrayNode);
+
+        // <item>
+        for (set<StringResource>::const_iterator item = items.begin();
+                item != items.end(); item++) {
+            XMLNode* itemNode = item->value->Clone();
+            itemNode->SetName("", "item");
+            itemNode->EditAttributes().clear();
+            arrayNode->EditChildren().push_back(itemNode);
+        }
+    }
+
+    // <string>
+    for (set<StringResource>::const_iterator it=m_strings.begin(); it!=m_strings.end(); it++) {
+        const StringResource& str = *it;
+        vector<XMLAttribute> attrs;
+        XMLNode* strNode = str.value->Clone();
+        strNode->SetName("", "string");
+        strNode->EditAttributes().clear();
+        strNode->EditAttributes().push_back(XMLAttribute("", "name", str.id));
+        root->EditChildren().push_back(strNode);
+    }
+
+    return root;
+}
+
+string
+ValuesFile::ToString() const
+{
+    XMLNode* xml = ToXMLNode();
+    string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+    s += xml->ToString(ANDROID_NAMESPACES);
+    delete xml;
+    s += '\n';
+    return s;
+}
+
diff --git a/tools/localize/ValuesFile.h b/tools/localize/ValuesFile.h
new file mode 100644
index 0000000..752fd78
--- /dev/null
+++ b/tools/localize/ValuesFile.h
@@ -0,0 +1,52 @@
+#ifndef VALUES_FILE_H
+#define VALUES_FILE_H
+
+#include "SourcePos.h"
+#include "Configuration.h"
+#include "XMLHandler.h"
+#include "Values.h"
+
+#include <string>
+#include <set>
+
+using namespace std;
+
+extern const XMLNamespaceMap ANDROID_NAMESPACES;
+
+class ValuesFile
+{
+public:
+    ValuesFile(const Configuration& config);
+
+    static ValuesFile* ParseFile(const string& filename, const Configuration& config,
+                                     int version, const string& versionString);
+    static ValuesFile* ParseString(const string& filename, const string& text,
+                                     const Configuration& config,
+                                     int version, const string& versionString);
+    ~ValuesFile();
+
+    const Configuration& GetConfiguration() const;
+
+    void AddString(const StringResource& str);
+    set<StringResource> GetStrings() const;
+
+    // exports this file as a n XMLNode, you own this object
+    XMLNode* ToXMLNode() const;
+
+    // writes the ValuesFile out to a string in the canonical format (i.e. writes the contents of
+    // ToXMLNode()).
+    string ToString() const;
+
+private:
+    class ParseState;
+    friend class ValuesFile::ParseState;
+    friend class StringHandler;
+
+    ValuesFile();
+
+    Configuration m_config;
+    set<StringResource> m_strings;
+    map<string,set<StringResource> > m_arrays;
+};
+
+#endif // VALUES_FILE_H
diff --git a/tools/localize/ValuesFile_test.cpp b/tools/localize/ValuesFile_test.cpp
new file mode 100644
index 0000000..56d2ec2
--- /dev/null
+++ b/tools/localize/ValuesFile_test.cpp
@@ -0,0 +1,54 @@
+#include "ValuesFile.h"
+#include <stdio.h>
+
+int
+ValuesFile_test()
+{
+    int err = 0;
+    Configuration config;
+    config.locale = "zz_ZZ";
+    ValuesFile* vf = ValuesFile::ParseFile("testdata/values/strings.xml", config,
+                                        OLD_VERSION, "1");
+
+    const set<StringResource>& strings = vf->GetStrings();
+    string canonical = vf->ToString();
+
+    if (false) {
+        printf("Strings (%zd)\n", strings.size());
+            for (set<StringResource>::const_iterator it=strings.begin();
+                    it!=strings.end(); it++) {
+            const StringResource& str = *it;
+            printf("%s: '%s'[%d]='%s' (%s) <!-- %s -->\n", str.pos.ToString().c_str(),
+                    str.id.c_str(), str.index,
+                    str.value->ContentsToString(ANDROID_NAMESPACES).c_str(),
+                    str.config.ToString().c_str(), str.comment.c_str());
+        }
+
+        printf("XML:[[%s]]\n", canonical.c_str());
+    }
+
+    const char * const EXPECTED =
+        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+        "<resources xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+        "    xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
+        "  <array name=\"emailAddressTypes\">\n"
+        "    <item>Email</item>\n"
+        "    <item>Home</item>\n"
+        "    <item>Work</item>\n"
+        "    <item>Other\\u2026</item>\n"
+        "  </array>\n"
+        "  <string name=\"test1\">Discard</string>\n"
+        "  <string name=\"test2\">a<b>b<i>c</i></b>d</string>\n"
+        "  <string name=\"test3\">a<xliff:g a=\"b\" xliff:a=\"asdf\">bBb</xliff:g>C</string>\n"
+        "</resources>\n";
+
+    if (canonical != EXPECTED) {
+        fprintf(stderr, "ValuesFile_test failed\n");
+        fprintf(stderr, "canonical=[[%s]]\n", canonical.c_str());
+        fprintf(stderr, "EXPECTED=[[%s]]\n", EXPECTED);
+        err = 1;
+    }
+
+    delete vf;
+    return err;
+}
diff --git a/tools/localize/XLIFFFile.cpp b/tools/localize/XLIFFFile.cpp
new file mode 100644
index 0000000..51f81de
--- /dev/null
+++ b/tools/localize/XLIFFFile.cpp
@@ -0,0 +1,609 @@
+#include "XLIFFFile.h"
+
+#include <algorithm>
+#include <sys/time.h>
+#include <time.h>
+
+const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
+
+const char *const NS_MAP[] = {
+    "", XLIFF_XMLNS,
+    "xml", XMLNS_XMLNS,
+    NULL, NULL
+};
+
+const XMLNamespaceMap XLIFF_NAMESPACES(NS_MAP);
+
+int
+XLIFFFile::File::Compare(const XLIFFFile::File& that) const
+{
+    if (filename != that.filename) {
+        return filename < that.filename ? -1 : 1;
+    }
+    return 0;
+}
+
+// =====================================================================================
+XLIFFFile::XLIFFFile()
+{
+}
+
+XLIFFFile::~XLIFFFile()
+{
+}
+
+static XMLNode*
+get_unique_node(const XMLNode* parent, const string& ns, const string& name, bool required)
+{
+    size_t count = parent->CountElementsByName(ns, name);
+    if (count == 1) {
+        return parent->GetElementByNameAt(ns, name, 0);
+    } else {
+        if (required) {
+            SourcePos pos = count == 0
+                                ? parent->Position()
+                                : parent->GetElementByNameAt(XLIFF_XMLNS, name, 1)->Position();
+            pos.Error("<%s> elements must contain exactly one <%s> element",
+                                parent->Name().c_str(), name.c_str());
+        }
+        return NULL;
+    }
+}
+
+XLIFFFile*
+XLIFFFile::Parse(const string& filename)
+{
+    XLIFFFile* result = new XLIFFFile();
+
+    XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY);
+    if (root == NULL) {
+        return NULL;
+    }
+
+    // <file>
+    vector<XMLNode*> files = root->GetElementsByName(XLIFF_XMLNS, "file");
+    for (size_t i=0; i<files.size(); i++) {
+        XMLNode* file = files[i];
+
+        string datatype = file->GetAttribute("", "datatype", "");
+        string originalFile = file->GetAttribute("", "original", "");
+
+        Configuration sourceConfig;
+        sourceConfig.locale = file->GetAttribute("", "source-language", "");
+        result->m_sourceConfig = sourceConfig;
+
+        Configuration targetConfig;
+        targetConfig.locale = file->GetAttribute("", "target-language", "");
+        result->m_targetConfig = targetConfig;
+
+        result->m_currentVersion = file->GetAttribute("", "build-num", "");
+        result->m_oldVersion = "old";
+
+        // <body>
+        XMLNode* body = get_unique_node(file, XLIFF_XMLNS, "body", true);
+        if (body == NULL) continue;
+
+        // <trans-unit>
+        vector<XMLNode*> transUnits = body->GetElementsByName(XLIFF_XMLNS, "trans-unit");
+        for (size_t j=0; j<transUnits.size(); j++) {
+            XMLNode* transUnit = transUnits[j];
+
+            string rawID = transUnit->GetAttribute("", "id", "");
+            if (rawID == "") {
+                transUnit->Position().Error("<trans-unit> tag requires an id");
+                continue;
+            }
+            string id;
+            int index;
+
+            if (!StringResource::ParseTypedID(rawID, &id, &index)) {
+                transUnit->Position().Error("<trans-unit> has invalid id '%s'\n", rawID.c_str());
+                continue;
+            }
+
+            // <source>
+            XMLNode* source = get_unique_node(transUnit, XLIFF_XMLNS, "source", false);
+            if (source != NULL) {
+                XMLNode* node = source->Clone();
+                node->SetPrettyRecursive(XMLNode::EXACT);
+                result->AddStringResource(StringResource(source->Position(), originalFile,
+                            sourceConfig, id, index, node, CURRENT_VERSION,
+                            result->m_currentVersion));
+            }
+
+            // <target>
+            XMLNode* target = get_unique_node(transUnit, XLIFF_XMLNS, "target", false);
+            if (target != NULL) {
+                XMLNode* node = target->Clone();
+                node->SetPrettyRecursive(XMLNode::EXACT);
+                result->AddStringResource(StringResource(target->Position(), originalFile,
+                            targetConfig, id, index, node, CURRENT_VERSION,
+                            result->m_currentVersion));
+            }
+
+            // <alt-trans>
+            XMLNode* altTrans = get_unique_node(transUnit, XLIFF_XMLNS, "alt-trans", false);
+            if (altTrans != NULL) {
+                // <source>
+                XMLNode* altSource = get_unique_node(altTrans, XLIFF_XMLNS, "source", false);
+                if (altSource != NULL) {
+                    XMLNode* node = altSource->Clone();
+                    node->SetPrettyRecursive(XMLNode::EXACT);
+                    result->AddStringResource(StringResource(altSource->Position(),
+                                originalFile, sourceConfig, id, index, node, OLD_VERSION,
+                                result->m_oldVersion));
+                }
+
+                // <target>
+                XMLNode* altTarget = get_unique_node(altTrans, XLIFF_XMLNS, "target", false);
+                if (altTarget != NULL) {
+                    XMLNode* node = altTarget->Clone();
+                    node->SetPrettyRecursive(XMLNode::EXACT);
+                    result->AddStringResource(StringResource(altTarget->Position(),
+                                originalFile, targetConfig, id, index, node, OLD_VERSION,
+                                result->m_oldVersion));
+                }
+            }
+        }
+    }
+    delete root;
+    return result;
+}
+
+XLIFFFile*
+XLIFFFile::Create(const Configuration& sourceConfig, const Configuration& targetConfig,
+                                const string& currentVersion)
+{
+    XLIFFFile* result = new XLIFFFile();
+        result->m_sourceConfig = sourceConfig;
+        result->m_targetConfig = targetConfig;
+        result->m_currentVersion = currentVersion;
+    return result;
+}
+
+set<string>
+XLIFFFile::Files() const
+{
+    set<string> result;
+    for (vector<File>::const_iterator f = m_files.begin(); f != m_files.end(); f++) {
+        result.insert(f->filename);
+    }
+    return result;
+}
+
+void
+XLIFFFile::AddStringResource(const StringResource& str)
+{
+    string id = str.TypedID();
+
+    File* f = NULL;
+    const size_t I = m_files.size();
+    for (size_t i=0; i<I; i++) {
+        if (m_files[i].filename == str.file) {
+            f = &m_files[i];
+            break;
+        }
+    }
+    if (f == NULL) {
+        File file;
+        file.filename = str.file;
+        m_files.push_back(file);
+        f = &m_files[I];
+    }
+
+    const size_t J = f->transUnits.size();
+    TransUnit* g = NULL;
+    for (size_t j=0; j<J; j++) {
+        if (f->transUnits[j].id == id) {
+            g = &f->transUnits[j];
+        }
+    }
+    if (g == NULL) {
+        TransUnit group;
+        group.id = id;
+        f->transUnits.push_back(group);
+        g = &f->transUnits[J];
+    }
+
+    StringResource* res = find_string_res(*g, str);
+    if (res == NULL) {
+        return ;
+    }
+    if (res->id != "") {
+        str.pos.Error("Duplicate string resource: %s", res->id.c_str());
+        res->pos.Error("Previous definition here");
+        return ;
+    }
+    *res = str;
+
+    m_strings.insert(str);
+}
+
+void
+XLIFFFile::Filter(bool (*func)(const string&,const TransUnit&,void*), void* cookie)
+{
+    const size_t I = m_files.size();
+    for (size_t ix=0, i=I-1; ix<I; ix++, i--) {
+        File& file = m_files[i];
+
+        const size_t J = file.transUnits.size();
+        for (size_t jx=0, j=J-1; jx<J; jx++, j--) {
+            TransUnit& tu = file.transUnits[j];
+
+            bool keep = func(file.filename, tu, cookie);
+            if (!keep) {
+                if (tu.source.id != "") {
+                    m_strings.erase(tu.source);
+                }
+                if (tu.target.id != "") {
+                    m_strings.erase(tu.target);
+                }
+                if (tu.altSource.id != "") {
+                    m_strings.erase(tu.altSource);
+                }
+                if (tu.altTarget.id != "") {
+                    m_strings.erase(tu.altTarget);
+                }
+                file.transUnits.erase(file.transUnits.begin()+j);
+            }
+        }
+        if (file.transUnits.size() == 0) {
+            m_files.erase(m_files.begin()+i);
+        }
+    }
+}
+
+void
+XLIFFFile::Map(void (*func)(const string&,TransUnit*,void*), void* cookie)
+{
+    const size_t I = m_files.size();
+    for (size_t i=0; i<I; i++) {
+        File& file = m_files[i];
+
+        const size_t J = file.transUnits.size();
+        for (size_t j=0; j<J; j++) {
+            func(file.filename, &(file.transUnits[j]), cookie);
+        }
+    }
+}
+
+TransUnit*
+XLIFFFile::EditTransUnit(const string& filename, const string& id)
+{
+    const size_t I = m_files.size();
+    for (size_t ix=0, i=I-1; ix<I; ix++, i--) {
+        File& file = m_files[i];
+        if (file.filename == filename) {
+            const size_t J = file.transUnits.size();
+            for (size_t jx=0, j=J-1; jx<J; jx++, j--) {
+                TransUnit& tu = file.transUnits[j];
+                if (tu.id == id) {
+                    return &tu;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+StringResource*
+XLIFFFile::find_string_res(TransUnit& g, const StringResource& str)
+{
+    int index;
+    if (str.version == CURRENT_VERSION) {
+        index = 0;
+    }
+    else if (str.version == OLD_VERSION) {
+        index = 2;
+    }
+    else {
+        str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__);
+        return NULL;
+    }
+    if (str.config == m_sourceConfig) {
+        // index += 0;
+    }
+    else if (str.config == m_targetConfig) {
+        index += 1;
+    }
+    else {
+        str.pos.Error("unknown config for string %s: %s", str.id.c_str(),
+                            str.config.ToString().c_str());
+        return NULL;
+    }
+    switch (index) {
+        case 0:
+            return &g.source;
+        case 1:
+            return &g.target;
+        case 2:
+            return &g.altSource;
+        case 3:
+            return &g.altTarget;
+    }
+    str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__);
+    return NULL;
+}
+
+int
+convert_html_to_xliff(const XMLNode* original, const string& name, XMLNode* addTo, int* phID)
+{
+    int err = 0;
+    if (original->Type() == XMLNode::TEXT) {
+        addTo->EditChildren().push_back(original->Clone());
+        return 0;
+    } else {
+        string ctype;
+        if (original->Namespace() == "") {
+            if (original->Name() == "b") {
+                ctype = "bold";
+            }
+            else if (original->Name() == "i") {
+                ctype = "italic";
+            }
+            else if (original->Name() == "u") {
+                ctype = "underline";
+            }
+        }
+        if (ctype != "") {
+            vector<XMLAttribute> attrs;
+            attrs.push_back(XMLAttribute(XLIFF_XMLNS, "ctype", ctype));
+            XMLNode* copy = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "g",
+                                                attrs, XMLNode::EXACT);
+
+            const vector<XMLNode*>& children = original->Children();
+            size_t I = children.size();
+            for (size_t i=0; i<I; i++) {
+                err |= convert_html_to_xliff(children[i], name, copy, phID);
+            }
+            return err;
+        }
+        else {
+            if (original->Namespace() == XLIFF_XMLNS) {
+                addTo->EditChildren().push_back(original->Clone());
+                return 0;
+            } else {
+                if (original->Namespace() == "") {
+                    // flatten out the tag into ph tags -- but only if there is no namespace
+                    // that's still unsupported because propagating the xmlns attribute is hard.
+                    vector<XMLAttribute> attrs;
+                    char idStr[30];
+                    (*phID)++;
+                    sprintf(idStr, "id-%d", *phID);
+                    attrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", idStr));
+
+                    if (original->Children().size() == 0) {
+                        XMLNode* ph = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
+                                "ph", attrs, XMLNode::EXACT);
+                        ph->EditChildren().push_back(
+                                XMLNode::NewText(original->Position(),
+                                    original->ToString(XLIFF_NAMESPACES),
+                                    XMLNode::EXACT));
+                        addTo->EditChildren().push_back(ph);
+                    } else {
+                        XMLNode* begin = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
+                                "bpt", attrs, XMLNode::EXACT);
+                        begin->EditChildren().push_back(
+                                XMLNode::NewText(original->Position(),
+                                    original->OpenTagToString(XLIFF_NAMESPACES, XMLNode::EXACT),
+                                    XMLNode::EXACT));
+                        XMLNode* end = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
+                                "ept", attrs, XMLNode::EXACT);
+                        string endText = "</";
+                            endText += original->Name();
+                            endText += ">";
+                        end->EditChildren().push_back(XMLNode::NewText(original->Position(),
+                                endText, XMLNode::EXACT));
+
+                        addTo->EditChildren().push_back(begin);
+
+                        const vector<XMLNode*>& children = original->Children();
+                        size_t I = children.size();
+                        for (size_t i=0; i<I; i++) {
+                            err |= convert_html_to_xliff(children[i], name, addTo, phID);
+                        }
+
+                        addTo->EditChildren().push_back(end);
+                    }
+                    return err;
+                } else {
+                    original->Position().Error("invalid <%s> element in <%s> tag\n",
+                                                original->Name().c_str(), name.c_str());
+                    return 1;
+                }
+            }
+        }
+    }
+}
+
+XMLNode*
+create_string_node(const StringResource& str, const string& name)
+{
+    vector<XMLAttribute> attrs;
+    attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve"));
+    XMLNode* node = XMLNode::NewElement(str.pos, XLIFF_XMLNS, name, attrs, XMLNode::EXACT);
+
+    const vector<XMLNode*>& children = str.value->Children();
+    size_t I = children.size();
+    int err = 0;
+    for (size_t i=0; i<I; i++) {
+        int phID = 0;
+        err |= convert_html_to_xliff(children[i], name, node, &phID);
+    }
+
+    if (err != 0) {
+        delete node;
+    }
+    return node;
+}
+
+static bool
+compare_id(const TransUnit& lhs, const TransUnit& rhs)
+{
+    string lid, rid;
+    int lindex, rindex;
+    StringResource::ParseTypedID(lhs.id, &lid, &lindex);
+    StringResource::ParseTypedID(rhs.id, &rid, &rindex);
+    if (lid < rid) return true;
+    if (lid == rid && lindex < rindex) return true;
+    return false;
+}
+
+XMLNode*
+XLIFFFile::ToXMLNode() const
+{
+    XMLNode* root;
+    size_t N;
+
+    // <xliff>
+    {
+        vector<XMLAttribute> attrs;
+        XLIFF_NAMESPACES.AddToAttributes(&attrs);
+        attrs.push_back(XMLAttribute(XLIFF_XMLNS, "version", "1.2"));
+        root = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "xliff", attrs, XMLNode::PRETTY);
+    }
+
+    vector<TransUnit> groups;
+
+    // <file>
+    vector<File> files = m_files;
+    sort(files.begin(), files.end());
+    const size_t I = files.size();
+    for (size_t i=0; i<I; i++) {
+        const File& file = files[i];
+
+        vector<XMLAttribute> fileAttrs;
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "datatype", "x-android-res"));
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "original", file.filename));
+
+        struct timeval tv;
+        struct timezone tz;
+        gettimeofday(&tv, &tz);
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "date", trim_string(ctime(&tv.tv_sec))));
+
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "source-language", m_sourceConfig.locale));
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "target-language", m_targetConfig.locale));
+        fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "build-num", m_currentVersion));
+
+        XMLNode* fileNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "file", fileAttrs,
+                                                XMLNode::PRETTY);
+        root->EditChildren().push_back(fileNode);
+
+        // <body>
+        XMLNode* bodyNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "body",
+                                                vector<XMLAttribute>(), XMLNode::PRETTY);
+        fileNode->EditChildren().push_back(bodyNode);
+
+        // <trans-unit>
+        vector<TransUnit> transUnits = file.transUnits;
+        sort(transUnits.begin(), transUnits.end(), compare_id);
+        const size_t J = transUnits.size();
+        for (size_t j=0; j<J; j++) {
+            const TransUnit& transUnit = transUnits[j];
+
+            vector<XMLAttribute> tuAttrs;
+
+            // strings start with string:
+            tuAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", transUnit.id));
+            XMLNode* transUnitNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "trans-unit",
+                                                         tuAttrs, XMLNode::PRETTY);
+            bodyNode->EditChildren().push_back(transUnitNode);
+
+            // <extradata>
+            if (transUnit.source.comment != "") {
+                vector<XMLAttribute> extradataAttrs;
+                XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "extradata",
+                                                            extradataAttrs, XMLNode::EXACT);
+                transUnitNode->EditChildren().push_back(extraNode);
+                extraNode->EditChildren().push_back(
+                        XMLNode::NewText(GENERATED_POS, transUnit.source.comment,
+                                         XMLNode::PRETTY));
+            }
+
+            // <source>
+            if (transUnit.source.id != "") {
+                transUnitNode->EditChildren().push_back(
+                                    create_string_node(transUnit.source, "source"));
+            }
+            
+            // <target>
+            if (transUnit.target.id != "") {
+                transUnitNode->EditChildren().push_back(
+                                    create_string_node(transUnit.target, "target"));
+            }
+
+            // <alt-trans>
+            if (transUnit.altSource.id != "" || transUnit.altTarget.id != ""
+                    || transUnit.rejectComment != "") {
+                vector<XMLAttribute> altTransAttrs;
+                XMLNode* altTransNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "alt-trans",
+                                                            altTransAttrs, XMLNode::PRETTY);
+                transUnitNode->EditChildren().push_back(altTransNode);
+
+                // <extradata>
+                if (transUnit.rejectComment != "") {
+                    vector<XMLAttribute> extradataAttrs;
+                    XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS,
+                                                                "extradata", extradataAttrs,
+                                                                XMLNode::EXACT);
+                    altTransNode->EditChildren().push_back(extraNode);
+                    extraNode->EditChildren().push_back(
+                            XMLNode::NewText(GENERATED_POS, transUnit.rejectComment,
+                                             XMLNode::PRETTY));
+                }
+                
+                // <source>
+                if (transUnit.altSource.id != "") {
+                    altTransNode->EditChildren().push_back(
+                                        create_string_node(transUnit.altSource, "source"));
+                }
+                
+                // <target>
+                if (transUnit.altTarget.id != "") {
+                    altTransNode->EditChildren().push_back(
+                                        create_string_node(transUnit.altTarget, "target"));
+                }
+            }
+            
+        }
+    }
+
+    return root;
+}
+
+
+string
+XLIFFFile::ToString() const
+{
+    XMLNode* xml = ToXMLNode();
+    string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+    s += xml->ToString(XLIFF_NAMESPACES);
+    delete xml;
+    s += '\n';
+    return s;
+}
+
+Stats
+XLIFFFile::GetStats(const string& config) const
+{
+    Stats stat;
+    stat.config = config;
+    stat.files = m_files.size();
+    stat.toBeTranslated = 0;
+    stat.noComments = 0;
+
+    for (vector<File>::const_iterator file=m_files.begin(); file!=m_files.end(); file++) {
+        stat.toBeTranslated += file->transUnits.size();
+
+        for (vector<TransUnit>::const_iterator tu=file->transUnits.begin();
+                    tu!=file->transUnits.end(); tu++) {
+            if (tu->source.comment == "") {
+                stat.noComments++;
+            }
+        }
+    }
+
+    stat.totalStrings = stat.toBeTranslated;
+
+    return stat;
+}
diff --git a/tools/localize/XLIFFFile.h b/tools/localize/XLIFFFile.h
new file mode 100644
index 0000000..a93d479
--- /dev/null
+++ b/tools/localize/XLIFFFile.h
@@ -0,0 +1,98 @@
+#ifndef XLIFF_FILE_H
+#define XLIFF_FILE_H
+
+#include "Values.h"
+
+#include "Configuration.h"
+
+#include <set>
+
+using namespace std;
+
+extern const XMLNamespaceMap XLIFF_NAMESPACES;
+
+extern const char*const XLIFF_XMLNS;
+
+struct Stats
+{
+    string config;
+    size_t files;
+    size_t toBeTranslated;
+    size_t noComments;
+    size_t totalStrings;
+};
+
+struct TransUnit {
+    string id;
+    StringResource source;
+    StringResource target;
+    StringResource altSource;
+    StringResource altTarget;
+    string rejectComment;
+};
+
+class XLIFFFile
+{
+public:
+    static XLIFFFile* Parse(const string& filename);
+    static XLIFFFile* Create(const Configuration& sourceConfig, const Configuration& targetConfig,
+                                const string& currentVersion);
+    ~XLIFFFile();
+
+    inline const Configuration& SourceConfig() const                { return m_sourceConfig; }
+    inline const Configuration& TargetConfig() const                { return m_targetConfig; }
+
+    inline const string& CurrentVersion() const                     { return m_currentVersion; }
+    inline const string& OldVersion() const                         { return m_oldVersion; }
+
+    set<string> Files() const;
+
+    void AddStringResource(const StringResource& res);
+    inline set<StringResource> const& GetStringResources() const { return m_strings; }
+    bool FindStringResource(const string& filename, int version, bool source);
+
+    void Filter(bool (*func)(const string&,const TransUnit&,void*), void* cookie);
+    void Map(void (*func)(const string&,TransUnit*,void*), void* cookie);
+
+    TransUnit* EditTransUnit(const string& file, const string& id);
+
+    // exports this file as a n XMLNode, you own this object
+    XMLNode* ToXMLNode() const;
+
+    // writes the ValuesFile out to a string in the canonical format (i.e. writes the contents of
+    // ToXMLNode()).
+    string ToString() const;
+
+    Stats GetStats(const string& config) const;
+
+private:
+    struct File {
+        int Compare(const File& that) const;
+
+        inline bool operator<(const File& that) const { return Compare(that) < 0; }
+        inline bool operator<=(const File& that) const { return Compare(that) <= 0; }
+        inline bool operator==(const File& that) const { return Compare(that) == 0; }
+        inline bool operator!=(const File& that) const { return Compare(that) != 0; }
+        inline bool operator>=(const File& that) const { return Compare(that) >= 0; }
+        inline bool operator>(const File& that) const { return Compare(that) > 0; }
+
+        string filename;
+        vector<TransUnit> transUnits;
+    };
+
+    XLIFFFile();
+    StringResource* find_string_res(TransUnit& g, const StringResource& str);
+    
+    Configuration m_sourceConfig;
+    Configuration m_targetConfig;
+
+    string m_currentVersion;
+    string m_oldVersion;
+
+    set<StringResource> m_strings;
+    vector<File> m_files;
+};
+
+int convert_html_to_xliff(const XMLNode* original, const string& name, XMLNode* addTo, int* phID);
+
+#endif // XLIFF_FILE_H
diff --git a/tools/localize/XLIFFFile_test.cpp b/tools/localize/XLIFFFile_test.cpp
new file mode 100644
index 0000000..52ed4c3
--- /dev/null
+++ b/tools/localize/XLIFFFile_test.cpp
@@ -0,0 +1,115 @@
+#include "XLIFFFile.h"
+#include <stdio.h>
+#include "ValuesFile.h"
+
+XMLNode* create_string_node(const StringResource& str, const string& name);
+
+static int
+Parse_test()
+{
+    XLIFFFile* xf = XLIFFFile::Parse("testdata/xliff1.xliff");
+    if (xf == NULL) {
+        return 1;
+    }
+
+    set<StringResource> const& strings = xf->GetStringResources();
+
+    if (false) {
+        for (set<StringResource>::iterator it=strings.begin(); it!=strings.end(); it++) {
+            const StringResource& str = *it;
+            printf("STRING!!! id=%s index=%d value='%s' pos=%s file=%s version=%d(%s)\n",
+                    str.id.c_str(), str.index,
+                    str.value->ContentsToString(ANDROID_NAMESPACES).c_str(),
+                    str.pos.ToString().c_str(), str.file.c_str(), str.version,
+                    str.versionString.c_str());
+        }
+        printf("XML:[[%s]]\n", xf->ToString().c_str());
+    }
+
+    delete xf;
+    return 0;
+}
+
+static XMLNode*
+add_html_tag(XMLNode* addTo, const string& tag)
+{
+    vector<XMLAttribute> attrs;
+    XMLNode* node = XMLNode::NewElement(GENERATED_POS, "", tag, attrs, XMLNode::EXACT);
+    addTo->EditChildren().push_back(node);
+    return node;
+}
+
+static int
+create_string_node_test()
+{
+    int err = 0;
+    StringResource res;
+    vector<XMLAttribute> attrs;
+    res.value = XMLNode::NewElement(GENERATED_POS, "", "something", attrs, XMLNode::EXACT);
+    res.value->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, " begin ", XMLNode::EXACT));
+
+    XMLNode* child;
+
+    child = add_html_tag(res.value, "b");
+    child->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, "b", XMLNode::EXACT));
+
+    child = add_html_tag(res.value, "i");
+    child->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, "i", XMLNode::EXACT));
+
+    child = add_html_tag(child, "b");
+    child->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, "b", XMLNode::EXACT));
+
+    child = add_html_tag(res.value, "u");
+    child->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, "u", XMLNode::EXACT));
+
+
+    res.value->EditChildren().push_back(XMLNode::NewText(GENERATED_POS, " end ", XMLNode::EXACT));
+
+    XMLNode* xliff = create_string_node(res, "blah");
+
+    string oldString = res.value->ToString(XLIFF_NAMESPACES);
+    string newString = xliff->ToString(XLIFF_NAMESPACES);
+
+    if (false) {
+        printf("OLD=\"%s\"\n", oldString.c_str());
+        printf("NEW=\"%s\"\n", newString.c_str());
+    }
+
+    const char* const EXPECTED_OLD
+                    = "<something> begin <b>b</b><i>i<b>b</b></i><u>u</u> end </something>";
+    if (oldString != EXPECTED_OLD) {
+        fprintf(stderr, "oldString mismatch:\n");
+        fprintf(stderr, "    expected='%s'\n", EXPECTED_OLD);
+        fprintf(stderr, "      actual='%s'\n", oldString.c_str());
+        err |= 1;
+    }
+
+    const char* const EXPECTED_NEW
+                    = "<blah xml:space=\"preserve\"> begin <g ctype=\"bold\">b</g>"
+                    "<g ctype=\"italic\">i<g ctype=\"bold\">b</g></g><g ctype=\"underline\">u</g>"
+                    " end </blah>";
+    if (newString != EXPECTED_NEW) {
+        fprintf(stderr, "newString mismatch:\n");
+        fprintf(stderr, "    expected='%s'\n", EXPECTED_NEW);
+        fprintf(stderr, "      actual='%s'\n", newString.c_str());
+        err |= 1;
+    }
+
+    if (err != 0) {
+        fprintf(stderr, "create_string_node_test failed\n");
+    }
+    return err;
+}
+
+int
+XLIFFFile_test()
+{
+    bool all = true;
+    int err = 0;
+
+    if (all) err |= Parse_test();
+    if (all) err |= create_string_node_test();
+
+    return err;
+}
+
diff --git a/tools/localize/XMLHandler.cpp b/tools/localize/XMLHandler.cpp
new file mode 100644
index 0000000..3fab211
--- /dev/null
+++ b/tools/localize/XMLHandler.cpp
@@ -0,0 +1,793 @@
+#include "XMLHandler.h"
+
+#include <algorithm>
+#include <expat.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define NS_SEPARATOR 1
+#define MORE_INDENT "  "
+
+static string
+xml_text_escape(const string& s)
+{
+    string result;
+    const size_t N = s.length();
+    for (size_t i=0; i<N; i++) {
+        char c = s[i];
+        switch (c) {
+            case '<':
+                result += "&lt;";
+                break;
+            case '>':
+                result += "&gt;";
+                break;
+            case '&':
+                result += "&amp;";
+                break;
+            default:
+                result += c;
+                break;
+        }
+    }
+    return result;
+}
+
+static string
+xml_attr_escape(const string& s)
+{
+    string result;
+    const size_t N = s.length();
+    for (size_t i=0; i<N; i++) {
+        char c = s[i];
+        switch (c) {
+            case '\"':
+                result += "&quot;";
+                break;
+            default:
+                result += c;
+                break;
+        }
+    }
+    return result;
+}
+
+XMLNamespaceMap::XMLNamespaceMap()
+{
+}
+
+XMLNamespaceMap::XMLNamespaceMap(char const*const* nspaces)
+
+{
+    while (*nspaces) {
+        m_map[nspaces[1]] = nspaces[0];
+        nspaces += 2;
+    }
+}
+
+string
+XMLNamespaceMap::Get(const string& ns) const
+{
+    if (ns == "xml") {
+        return ns;
+    }
+    map<string,string>::const_iterator it = m_map.find(ns);
+    if (it == m_map.end()) {
+        return "";
+    } else {
+        return it->second;
+    }
+}
+
+string
+XMLNamespaceMap::GetPrefix(const string& ns) const
+{
+    if (ns == "") {
+        return "";
+    }
+    map<string,string>::const_iterator it = m_map.find(ns);
+    if (it != m_map.end()) {
+        if (it->second == "") {
+            return "";
+        } else {
+            return it->second + ":";
+        }
+    } else {
+        return ":"; // invalid
+    }
+}
+
+void
+XMLNamespaceMap::AddToAttributes(vector<XMLAttribute>* attrs) const
+{
+    map<string,string>::const_iterator it;
+    for (it=m_map.begin(); it!=m_map.end(); it++) {
+        if (it->second == "xml") {
+            continue;
+        }
+        XMLAttribute attr;
+        if (it->second == "") {
+            attr.name = "xmlns";
+        } else {
+            attr.name = "xmlns:";
+            attr.name += it->second;
+        }
+        attr.value = it->first;
+        attrs->push_back(attr);
+    }
+}
+
+XMLAttribute::XMLAttribute()
+{
+}
+
+XMLAttribute::XMLAttribute(const XMLAttribute& that)
+    :ns(that.ns),
+     name(that.name),
+     value(that.value)
+{
+}
+
+XMLAttribute::XMLAttribute(string n, string na, string v)
+    :ns(n),
+     name(na),
+     value(v)
+{
+}
+
+XMLAttribute::~XMLAttribute()
+{
+}
+
+int
+XMLAttribute::Compare(const XMLAttribute& that) const
+{
+    if (ns != that.ns) {
+        return ns < that.ns ? -1 : 1;
+    }
+    if (name != that.name) {
+        return name < that.name ? -1 : 1;
+    }
+    return 0;
+}
+
+string
+XMLAttribute::Find(const vector<XMLAttribute>& list, const string& ns, const string& name,
+                    const string& def)
+{
+    const size_t N = list.size();
+    for (size_t i=0; i<N; i++) {
+        const XMLAttribute& attr = list[i];
+        if (attr.ns == ns && attr.name == name) {
+            return attr.value;
+        }
+    }
+    return def;
+}
+
+struct xml_handler_data {
+    vector<XMLHandler*> stack;
+    XML_Parser parser;
+    vector<vector<XMLAttribute>*> attributes;
+    string filename;
+};
+
+XMLNode::XMLNode()
+{
+}
+
+XMLNode::~XMLNode()
+{
+//    for_each(m_children.begin(), m_children.end(), delete_object<XMLNode>);
+}
+
+XMLNode*
+XMLNode::Clone() const
+{
+    switch (m_type) {
+        case ELEMENT: {
+            XMLNode* e = XMLNode::NewElement(m_pos, m_ns, m_name, m_attrs, m_pretty);
+            const size_t N = m_children.size();
+            for (size_t i=0; i<N; i++) {
+                e->m_children.push_back(m_children[i]->Clone());
+            }
+            return e;
+        }
+        case TEXT: {
+            return XMLNode::NewText(m_pos, m_text, m_pretty);
+        }
+        default:
+            return NULL;
+    }
+}
+
+XMLNode*
+XMLNode::NewElement(const SourcePos& pos, const string& ns, const string& name,
+                        const vector<XMLAttribute>& attrs, int pretty)
+{
+    XMLNode* node = new XMLNode();
+        node->m_type = ELEMENT;
+        node->m_pretty = pretty;
+        node->m_pos = pos;
+        node->m_ns = ns;
+        node->m_name = name;
+        node->m_attrs = attrs;
+    return node;
+}
+
+XMLNode*
+XMLNode::NewText(const SourcePos& pos, const string& text, int pretty)
+{
+    XMLNode* node = new XMLNode();
+        node->m_type = TEXT;
+        node->m_pretty = pretty;
+        node->m_pos = pos;
+        node->m_text = text;
+    return node;
+}
+
+void
+XMLNode::SetPrettyRecursive(int value)
+{
+    m_pretty = value;
+    const size_t N = m_children.size();
+    for (size_t i=0; i<N; i++) {
+        m_children[i]->SetPrettyRecursive(value);
+    }
+}
+
+string
+XMLNode::ContentsToString(const XMLNamespaceMap& nspaces) const
+{
+    return contents_to_string(nspaces, "");
+}
+
+string
+XMLNode::ToString(const XMLNamespaceMap& nspaces) const
+{
+    return to_string(nspaces, "");
+}
+
+string
+XMLNode::OpenTagToString(const XMLNamespaceMap& nspaces, int pretty) const
+{
+    return open_tag_to_string(nspaces, "", pretty);
+}
+
+string
+XMLNode::contents_to_string(const XMLNamespaceMap& nspaces, const string& indent) const
+{
+    string result;
+    const size_t N = m_children.size();
+    for (size_t i=0; i<N; i++) {
+        const XMLNode* child = m_children[i];
+        switch (child->Type()) {
+        case ELEMENT:
+            if (m_pretty == PRETTY) {
+                result += '\n';
+                result += indent;
+            }
+        case TEXT:
+            result += child->to_string(nspaces, indent);
+            break;
+        }
+    }
+    return result;
+}
+
+string
+trim_string(const string& str)
+{
+    const char* p = str.c_str();
+    while (*p && isspace(*p)) {
+        p++;
+    }
+    const char* q = str.c_str() + str.length() - 1;
+    while (q > p && isspace(*q)) {
+        q--;
+    }
+    q++;
+    return string(p, q-p);
+}
+
+string
+XMLNode::open_tag_to_string(const XMLNamespaceMap& nspaces, const string& indent, int pretty) const
+{
+    if (m_type != ELEMENT) {
+        return "";
+    }
+    string result = "<";
+    result += nspaces.GetPrefix(m_ns);
+    result += m_name;
+
+    vector<XMLAttribute> attrs = m_attrs;
+
+    sort(attrs.begin(), attrs.end());
+
+    const size_t N = attrs.size();
+    for (size_t i=0; i<N; i++) {
+        const XMLAttribute& attr = attrs[i];
+        if (i == 0 || m_pretty == EXACT || pretty == EXACT) {
+            result += ' ';
+        }
+        else {
+            result += "\n";
+            result += indent;
+            result += MORE_INDENT;
+            result += MORE_INDENT;
+        }
+        result += nspaces.GetPrefix(attr.ns);
+        result += attr.name;
+        result += "=\"";
+        result += xml_attr_escape(attr.value);
+        result += '\"';
+    }
+
+    if (m_children.size() > 0) {
+        result += '>';
+    } else {
+        result += " />";
+    }
+    return result;
+}
+
+string
+XMLNode::to_string(const XMLNamespaceMap& nspaces, const string& indent) const
+{
+    switch (m_type)
+    {
+        case TEXT: {
+            if (m_pretty == EXACT) {
+                return xml_text_escape(m_text);
+            } else {
+                return xml_text_escape(trim_string(m_text));
+            }
+        }
+        case ELEMENT: {
+            string result = open_tag_to_string(nspaces, indent, PRETTY);
+            
+            if (m_children.size() > 0) {
+                result += contents_to_string(nspaces, indent + MORE_INDENT);
+
+                if (m_pretty == PRETTY && m_children.size() > 0) {
+                    result += '\n';
+                    result += indent;
+                }
+
+                result += "</";
+                result += nspaces.GetPrefix(m_ns);
+                result += m_name;
+                result += '>';
+            }
+            return result;
+        }
+        default:
+            return "";
+    }
+}
+
+string
+XMLNode::CollapseTextContents() const
+{
+    if (m_type == TEXT) {
+        return m_text;
+    }
+    else if (m_type == ELEMENT) {
+        string result;
+
+        const size_t N=m_children.size();
+        for (size_t i=0; i<N; i++) {
+            result += m_children[i]->CollapseTextContents();
+        }
+
+        return result;
+    }
+    else {
+        return "";
+    }
+}
+
+vector<XMLNode*>
+XMLNode::GetElementsByName(const string& ns, const string& name) const
+{
+    vector<XMLNode*> result;
+    const size_t N=m_children.size();
+    for (size_t i=0; i<N; i++) {
+        XMLNode* child = m_children[i];
+        if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+            result.push_back(child);
+        }
+    }
+    return result;
+}
+
+XMLNode*
+XMLNode::GetElementByNameAt(const string& ns, const string& name, size_t index) const
+{
+    vector<XMLNode*> result;
+    const size_t N=m_children.size();
+    for (size_t i=0; i<N; i++) {
+        XMLNode* child = m_children[i];
+        if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+            if (index == 0) {
+                return child;
+            } else {
+                index--;
+            }
+        }
+    }
+    return NULL;
+}
+
+size_t
+XMLNode::CountElementsByName(const string& ns, const string& name) const
+{
+    size_t result = 0;
+    const size_t N=m_children.size();
+    for (size_t i=0; i<N; i++) {
+        XMLNode* child = m_children[i];
+        if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+            result++;
+        }
+    }
+    return result;
+}
+
+string
+XMLNode::GetAttribute(const string& ns, const string& name, const string& def) const
+{
+    return XMLAttribute::Find(m_attrs, ns, name, def);
+}
+
+static void
+parse_namespace(const char* data, string* ns, string* name)
+{
+    const char* p = strchr(data, NS_SEPARATOR);
+    if (p != NULL) {
+        ns->assign(data, p-data);
+        name->assign(p+1);
+    } else {
+        ns->assign("");
+        name->assign(data);
+    }
+}
+
+static void
+convert_attrs(const char** in, vector<XMLAttribute>* out)
+{
+    while (*in) {
+        XMLAttribute attr;
+        parse_namespace(in[0], &attr.ns, &attr.name);
+        attr.value = in[1];
+        out->push_back(attr);
+        in += 2;
+    }
+}
+
+static bool
+list_contains(const vector<XMLHandler*>& stack, XMLHandler* handler)
+{
+    const size_t N = stack.size();
+    for (size_t i=0; i<N; i++) {
+        if (stack[i] == handler) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void XMLCALL
+start_element_handler(void *userData, const char *name, const char **attrs)
+{
+    xml_handler_data* data = (xml_handler_data*)userData;
+
+    XMLHandler* handler = data->stack[data->stack.size()-1];
+
+    SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+    string nsString;
+    string nameString;
+    XMLHandler* next = handler;
+    vector<XMLAttribute> attributes;
+
+    parse_namespace(name, &nsString, &nameString);
+    convert_attrs(attrs, &attributes);
+
+    handler->OnStartElement(pos, nsString, nameString, attributes, &next);
+
+    if (next == NULL) {
+        next = handler;
+    }
+
+    if (next != handler) {
+        next->elementPos = pos;
+        next->elementNamespace = nsString;
+        next->elementName = nameString;
+        next->elementAttributes = attributes;
+    }
+
+    data->stack.push_back(next);
+}
+
+static void XMLCALL
+end_element_handler(void *userData, const char *name)
+{
+    xml_handler_data* data = (xml_handler_data*)userData;
+
+    XMLHandler* handler = data->stack[data->stack.size()-1];
+    data->stack.pop_back();
+
+    SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+
+    if (!list_contains(data->stack, handler)) {
+        handler->OnDone(pos);
+        if (data->stack.size() > 1) {
+            // not top one
+            delete handler;
+        }
+    }
+
+    handler = data->stack[data->stack.size()-1];
+
+    string nsString;
+    string nameString;
+
+    parse_namespace(name, &nsString, &nameString);
+
+    handler->OnEndElement(pos, nsString, nameString);
+}
+
+static void XMLCALL
+text_handler(void *userData, const XML_Char *s, int len)
+{
+    xml_handler_data* data = (xml_handler_data*)userData;
+    XMLHandler* handler = data->stack[data->stack.size()-1];
+    SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+    handler->OnText(pos, string(s, len));
+}
+
+static void XMLCALL
+comment_handler(void *userData, const char *comment)
+{
+    xml_handler_data* data = (xml_handler_data*)userData;
+    XMLHandler* handler = data->stack[data->stack.size()-1];
+    SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+    handler->OnComment(pos, string(comment));
+}
+
+bool
+XMLHandler::ParseFile(const string& filename, XMLHandler* handler)
+{
+    char buf[16384];
+    int fd = open(filename.c_str(), O_RDONLY);
+    if (fd < 0) {
+        SourcePos(filename, -1).Error("Unable to open file for read: %s", strerror(errno));
+        return false;
+    }
+
+    XML_Parser parser = XML_ParserCreateNS(NULL, NS_SEPARATOR);
+    xml_handler_data state;
+    state.stack.push_back(handler);
+    state.parser = parser;
+    state.filename = filename;
+
+    XML_SetUserData(parser, &state);
+    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
+    XML_SetCharacterDataHandler(parser, text_handler);
+    XML_SetCommentHandler(parser, comment_handler);
+
+    ssize_t len;
+    bool done;
+    do {
+        len = read(fd, buf, sizeof(buf));
+        done = len < (ssize_t)sizeof(buf);
+        if (len < 0) {
+            SourcePos(filename, -1).Error("Error reading file: %s\n", strerror(errno));
+            close(fd);
+            return false;
+        }
+        if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
+            SourcePos(filename, (int)XML_GetCurrentLineNumber(parser)).Error(
+                    "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+            close(fd);
+            return false;
+        }
+    } while (!done);
+
+    XML_ParserFree(parser);
+
+    close(fd);
+    
+    return true;
+}
+
+bool
+XMLHandler::ParseString(const string& filename, const string& text, XMLHandler* handler)
+{
+    XML_Parser parser = XML_ParserCreateNS(NULL, NS_SEPARATOR);
+    xml_handler_data state;
+    state.stack.push_back(handler);
+    state.parser = parser;
+    state.filename = filename;
+
+    XML_SetUserData(parser, &state);
+    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
+    XML_SetCharacterDataHandler(parser, text_handler);
+    XML_SetCommentHandler(parser, comment_handler);
+
+    if (XML_Parse(parser, text.c_str(), text.size(), true) == XML_STATUS_ERROR) {
+        SourcePos(filename, (int)XML_GetCurrentLineNumber(parser)).Error(
+                "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+        return false;
+    }
+
+    XML_ParserFree(parser);
+    
+    return true;
+}
+
+XMLHandler::XMLHandler()
+{
+}
+
+XMLHandler::~XMLHandler()
+{
+}
+
+int
+XMLHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                            const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+    return 0;
+}
+
+int
+XMLHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+    return 0;
+}
+
+int
+XMLHandler::OnText(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+XMLHandler::OnComment(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+XMLHandler::OnDone(const SourcePos& pos)
+{
+    return 0;
+}
+
+TopElementHandler::TopElementHandler(const string& ns, const string& name, XMLHandler* next)
+    :m_ns(ns),
+     m_name(name),
+     m_next(next)
+{
+}
+
+int
+TopElementHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                            const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+    *next = m_next;
+    return 0;
+}
+
+int
+TopElementHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+    return 0;
+}
+
+int
+TopElementHandler::OnText(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+TopElementHandler::OnDone(const SourcePos& pos)
+{
+    return 0;
+}
+
+
+NodeHandler::NodeHandler(XMLNode* root, int pretty)
+    :m_root(root),
+     m_pretty(pretty)
+{
+    if (root != NULL) {
+        m_nodes.push_back(root);
+    }
+}
+
+NodeHandler::~NodeHandler()
+{
+}
+
+int
+NodeHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                            const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+    int pretty;
+    if (XMLAttribute::Find(attrs, XMLNS_XMLNS, "space", "") == "preserve") {
+        pretty = XMLNode::EXACT;
+    } else {
+        if (m_root == NULL) {
+            pretty = m_pretty;
+        } else {
+            pretty = m_nodes[m_nodes.size()-1]->Pretty();
+        }
+    }
+    XMLNode* n = XMLNode::NewElement(pos, ns, name, attrs, pretty);
+    if (m_root == NULL) {
+        m_root = n;
+    } else {
+        m_nodes[m_nodes.size()-1]->EditChildren().push_back(n);
+    }
+    m_nodes.push_back(n);
+    return 0;
+}
+
+int
+NodeHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+    m_nodes.pop_back();
+    return 0;
+}
+
+int
+NodeHandler::OnText(const SourcePos& pos, const string& text)
+{
+    if (m_root == NULL) {
+        return 1;
+    }
+    XMLNode* n = XMLNode::NewText(pos, text, m_nodes[m_nodes.size()-1]->Pretty());
+    m_nodes[m_nodes.size()-1]->EditChildren().push_back(n);
+    return 0;
+}
+
+int
+NodeHandler::OnComment(const SourcePos& pos, const string& text)
+{
+    return 0;
+}
+
+int
+NodeHandler::OnDone(const SourcePos& pos)
+{
+    return 0;
+}
+
+XMLNode*
+NodeHandler::ParseFile(const string& filename, int pretty)
+{
+    NodeHandler handler(NULL, pretty);
+    if (!XMLHandler::ParseFile(filename, &handler)) {
+        fprintf(stderr, "error parsing file: %s\n", filename.c_str());
+        return NULL;
+    }
+    return handler.Root();
+}
+
+XMLNode*
+NodeHandler::ParseString(const string& filename, const string& text, int pretty)
+{
+    NodeHandler handler(NULL, pretty);
+    if (!XMLHandler::ParseString(filename, text, &handler)) {
+        fprintf(stderr, "error parsing file: %s\n", filename.c_str());
+        return NULL;
+    }
+    return handler.Root();
+}
+
+
diff --git a/tools/localize/XMLHandler.h b/tools/localize/XMLHandler.h
new file mode 100644
index 0000000..1130710
--- /dev/null
+++ b/tools/localize/XMLHandler.h
@@ -0,0 +1,197 @@
+#ifndef XML_H
+#define XML_H
+
+#include "SourcePos.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#define XMLNS_XMLNS "http://www.w3.org/XML/1998/namespace"
+
+using namespace std;
+
+string trim_string(const string& str);
+
+struct XMLAttribute
+{
+    string ns;
+    string name;
+    string value;
+
+    XMLAttribute();
+    XMLAttribute(const XMLAttribute& that);
+    XMLAttribute(string ns, string name, string value);
+    ~XMLAttribute();
+
+    int Compare(const XMLAttribute& that) const;
+
+    inline bool operator<(const XMLAttribute& that) const { return Compare(that) < 0; }
+    inline bool operator<=(const XMLAttribute& that) const { return Compare(that) <= 0; }
+    inline bool operator==(const XMLAttribute& that) const { return Compare(that) == 0; }
+    inline bool operator!=(const XMLAttribute& that) const { return Compare(that) != 0; }
+    inline bool operator>=(const XMLAttribute& that) const { return Compare(that) >= 0; }
+    inline bool operator>(const XMLAttribute& that) const { return Compare(that) > 0; }
+
+    static string Find(const vector<XMLAttribute>& list,
+                                const string& ns, const string& name, const string& def);
+};
+
+class XMLNamespaceMap
+{
+public:
+    XMLNamespaceMap();
+    XMLNamespaceMap(char const*const* nspaces);
+    string Get(const string& ns) const;
+    string GetPrefix(const string& ns) const;
+    void AddToAttributes(vector<XMLAttribute>* attrs) const;
+private:
+    map<string,string> m_map;
+};
+
+struct XMLNode
+{
+public:
+    enum {
+        EXACT = 0,
+        PRETTY = 1
+    };
+
+    enum {
+        ELEMENT = 0,
+        TEXT = 1
+    };
+
+    static XMLNode* NewElement(const SourcePos& pos, const string& ns, const string& name,
+                        const vector<XMLAttribute>& attrs, int pretty);
+    static XMLNode* NewText(const SourcePos& pos, const string& text, int pretty);
+
+    ~XMLNode();
+
+    // a deep copy
+    XMLNode* Clone() const;
+
+    inline int Type() const                                     { return m_type; }
+    inline int Pretty() const                                   { return m_pretty; }
+    void SetPrettyRecursive(int value);
+    string ContentsToString(const XMLNamespaceMap& nspaces) const;
+    string ToString(const XMLNamespaceMap& nspaces) const;
+    string OpenTagToString(const XMLNamespaceMap& nspaces, int pretty) const;
+
+    string CollapseTextContents() const;
+
+    inline const SourcePos& Position() const                    { return m_pos; }
+
+    // element
+    inline string Namespace() const                             { return m_ns; }
+    inline string Name() const                                  { return m_name; }
+    inline void SetName(const string& ns, const string& n)      { m_ns = ns; m_name = n; }
+    inline const vector<XMLAttribute>& Attributes() const       { return m_attrs; }
+    inline vector<XMLAttribute>& EditAttributes()               { return m_attrs; }
+    inline const vector<XMLNode*>& Children() const             { return m_children; }
+    inline vector<XMLNode*>& EditChildren()                     { return m_children; }
+    vector<XMLNode*> GetElementsByName(const string& ns, const string& name) const;
+    XMLNode* GetElementByNameAt(const string& ns, const string& name, size_t index) const;
+    size_t CountElementsByName(const string& ns, const string& name) const;
+    string GetAttribute(const string& ns, const string& name, const string& def) const;
+
+    // text
+    inline string Text() const                                  { return m_text; }
+
+private:
+    XMLNode();
+    XMLNode(const XMLNode&);
+
+    string contents_to_string(const XMLNamespaceMap& nspaces, const string& indent) const;
+    string to_string(const XMLNamespaceMap& nspaces, const string& indent) const;
+    string open_tag_to_string(const XMLNamespaceMap& nspaces, const string& indent,
+            int pretty) const;
+
+    int m_type;
+    int m_pretty;
+    SourcePos m_pos;
+
+    // element
+    string m_ns;
+    string m_name;
+    vector<XMLAttribute> m_attrs;
+    vector<XMLNode*> m_children;
+
+    // text
+    string m_text;
+};
+
+class XMLHandler
+{
+public:
+    // information about the element that started us
+    SourcePos elementPos;
+    string elementNamespace;
+    string elementName;
+    vector<XMLAttribute> elementAttributes;
+
+    XMLHandler();
+    virtual ~XMLHandler();
+
+    XMLHandler* parent;
+
+    virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next);
+    virtual int OnEndElement(const SourcePos& pos, const string& ns, const string& name);
+    virtual int OnText(const SourcePos& pos, const string& text);
+    virtual int OnComment(const SourcePos& pos, const string& text);
+    virtual int OnDone(const SourcePos& pos);
+
+    static bool ParseFile(const string& filename, XMLHandler* handler);
+    static bool ParseString(const string& filename, const string& text, XMLHandler* handler);
+};
+
+class TopElementHandler : public XMLHandler
+{
+public:
+    TopElementHandler(const string& ns, const string& name, XMLHandler* next);
+
+    virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next);
+    virtual int OnEndElement(const SourcePos& pos, const string& ns, const string& name);
+    virtual int OnText(const SourcePos& pos, const string& text);
+    virtual int OnDone(const SourcePos& endPos);
+
+private:
+    string m_ns;
+    string m_name;
+    XMLHandler* m_next;
+};
+
+class NodeHandler : public XMLHandler
+{
+public:
+    // after it's done, you own everything created and added to root
+    NodeHandler(XMLNode* root, int pretty);
+    ~NodeHandler();
+
+    virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+                                const vector<XMLAttribute>& attrs, XMLHandler** next);
+    virtual int OnEndElement(const SourcePos& pos, const string& ns, const string& name);
+    virtual int OnText(const SourcePos& pos, const string& text);
+    virtual int OnComment(const SourcePos& pos, const string& text);
+    virtual int OnDone(const SourcePos& endPos);
+
+    inline XMLNode* Root() const                { return m_root; }
+
+    static XMLNode* ParseFile(const string& filename, int pretty);
+    static XMLNode* ParseString(const string& filename, const string& text, int pretty);
+
+private:
+    XMLNode* m_root;
+    int m_pretty;
+    vector<XMLNode*> m_nodes;
+};
+
+template <class T>
+static void delete_object(T* obj)
+{
+    delete obj;
+}
+
+#endif // XML_H
diff --git a/tools/localize/XMLHandler_test.cpp b/tools/localize/XMLHandler_test.cpp
new file mode 100644
index 0000000..1c81c0c
--- /dev/null
+++ b/tools/localize/XMLHandler_test.cpp
@@ -0,0 +1,133 @@
+#include "XMLHandler.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+const char *const NS_MAP[] = {
+    "xml", XMLNS_XMLNS,
+    NULL, NULL
+};
+
+const XMLNamespaceMap NO_NAMESPACES(NS_MAP);
+
+char const*const EXPECTED_EXACT = 
+       "<ASDF>\n"
+        "    <a id=\"system\" old-cl=\"1\" new-cl=\"43019\">\n"
+        "        <app dir=\"apps/common\" />\n"
+        "    </a>\n"
+        "    <a id=\"samples\" old-cl=\"1\" new-cl=\"43019\">asdf\n"
+        "        <app dir=\"samples/NotePad\" />\n"
+        "        <app dir=\"samples/LunarLander\" />\n"
+        "        <something>a<b>,</b>b </something>\n"
+        "        <exact xml:space=\"preserve\">a<b>,</b>b </exact>\n"
+        "    </a>\n"
+        "</ASDF>\n";
+
+char const*const EXPECTED_PRETTY =
+        "<ASDF>\n"
+        "  <a id=\"system\"\n"
+        "      old-cl=\"1\"\n"
+        "      new-cl=\"43019\">\n"
+        "    <app dir=\"apps/common\" />\n"
+        "  </a>\n"
+        "  <a id=\"samples\"\n"
+        "      old-cl=\"1\"\n"
+        "      new-cl=\"43019\">asdf\n"
+        "    <app dir=\"samples/NotePad\" />\n"
+        "    <app dir=\"samples/LunarLander\" />\n"
+        "    <something>a\n"
+        "      <b>,\n"
+        "      </b>b \n"
+        "    </something>\n"
+        "    <exact xml:space=\"preserve\">a<b>,</b>b </exact>\n"
+        "  </a>\n"
+        "</ASDF>\n";
+
+static string
+read_file(const string& filename)
+{
+    char buf[1024];
+    int fd = open(filename.c_str(), O_RDONLY);
+    if (fd < 0) {
+        return "";
+    }
+    string result;
+    while (true) {
+        ssize_t len = read(fd, buf, sizeof(buf)-1);
+        buf[len] = '\0';
+        if (len <= 0) {
+            break;
+        }
+        result.append(buf, len);
+    }
+    close(fd);
+    return result;
+}
+
+static int
+ParseFile_EXACT_test()
+{
+    XMLNode* root = NodeHandler::ParseFile("testdata/xml.xml", XMLNode::EXACT);
+    if (root == NULL) {
+        return 1;
+    }
+    string result = root->ToString(NO_NAMESPACES);
+    delete root;
+    //printf("[[%s]]\n", result.c_str());
+    return result == EXPECTED_EXACT;
+}
+
+static int
+ParseFile_PRETTY_test()
+{
+    XMLNode* root = NodeHandler::ParseFile("testdata/xml.xml", XMLNode::PRETTY);
+    if (root == NULL) {
+        return 1;
+    }
+    string result = root->ToString(NO_NAMESPACES);
+    delete root;
+    //printf("[[%s]]\n", result.c_str());
+    return result == EXPECTED_PRETTY;
+}
+
+static int
+ParseString_EXACT_test()
+{
+    string text = read_file("testdata/xml.xml");
+    XMLNode* root = NodeHandler::ParseString("testdata/xml.xml", text, XMLNode::EXACT);
+    if (root == NULL) {
+        return 1;
+    }
+    string result = root->ToString(NO_NAMESPACES);
+    delete root;
+    //printf("[[%s]]\n", result.c_str());
+    return result == EXPECTED_EXACT;
+}
+
+static int
+ParseString_PRETTY_test()
+{
+    string text = read_file("testdata/xml.xml");
+    XMLNode* root = NodeHandler::ParseString("testdata/xml.xml", text, XMLNode::PRETTY);
+    if (root == NULL) {
+        return 1;
+    }
+    string result = root->ToString(NO_NAMESPACES);
+    delete root;
+    //printf("[[%s]]\n", result.c_str());
+    return result == EXPECTED_PRETTY;
+}
+
+int
+XMLHandler_test()
+{
+    int err = 0;
+    bool all = true;
+
+    if (all) err |= ParseFile_EXACT_test();
+    if (all) err |= ParseFile_PRETTY_test();
+    if (all) err |= ParseString_EXACT_test();
+    if (all) err |= ParseString_PRETTY_test();
+
+    return err;
+}
diff --git a/tools/localize/XMLNode.h b/tools/localize/XMLNode.h
new file mode 100644
index 0000000..bfb9f55
--- /dev/null
+++ b/tools/localize/XMLNode.h
@@ -0,0 +1,19 @@
+#ifndef XMLNODE_H
+#define XMLNODE_H
+
+#include <string>
+
+using namespace std;
+
+struct XMLAttribute
+{
+    string ns;
+    string name;
+    string value;
+
+    static string Find(const vector<XMLAttribute>& list,
+                                const string& ns, const string& name, const string& def);
+};
+
+
+#endif // XMLNODE_H
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
new file mode 100644
index 0000000..bb82a9c
--- /dev/null
+++ b/tools/localize/file_utils.cpp
@@ -0,0 +1,143 @@
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "file_utils.h"
+#include "Perforce.h"
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <host/Directories.h>
+#include "log.h"
+
+string
+translated_file_name(const string& file, const string& locale)
+{
+    const char* str = file.c_str();
+    const char* p = str + file.length();
+    const char* rest = NULL;
+    const char* values = p;
+
+    while (p > str) {
+        p--;
+        if (*p == '/') {
+            rest = values;
+            values = p;
+            if (0 == strncmp("values", values+1, rest-values-1)) {
+                break;
+            }
+        }
+    }
+    values++;
+
+    string result(str, values-str);
+    result.append(values, rest-values);
+
+    string language, region;
+    if (locale == "") {
+        language = "";
+        region = "";
+    }
+    else if (!split_locale(locale, &language, &region)) {
+        return "";
+    }
+
+    if (language != "") {
+        result += '-';
+        result += language;
+    }
+    if (region != "") {
+        result += "-r";
+        result += region;
+    }
+
+    result += rest;
+
+    return result;
+}
+
+ValuesFile*
+get_values_file(const string& filename, const Configuration& configuration,
+                int version, const string& versionString, bool printOnFailure)
+{
+    int err;
+    string text;
+
+    log_printf("get_values_file filename=%s\n", filename.c_str());
+    err = Perforce::GetFile(filename, versionString, &text, printOnFailure);
+    if (err != 0 || text == "") {
+        return NULL;
+    }
+
+    ValuesFile* result = ValuesFile::ParseString(filename, text, configuration, version,
+                                                    versionString);
+    if (result == NULL) {
+        fprintf(stderr, "unable to parse file: %s\n", filename.c_str());
+        exit(1);
+    }
+    return result;
+}
+
+ValuesFile*
+get_local_values_file(const string& filename, const Configuration& configuration,
+                int version, const string& versionString, bool printOnFailure)
+{
+    int err;
+    string text;
+    char buf[2049];
+    int fd;
+    ssize_t amt;
+    
+    fd = open(filename.c_str(), O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "unable to open file: %s\n", filename.c_str());
+        return NULL;
+    }
+
+    while ((amt = read(fd, buf, sizeof(buf)-1)) > 0) {
+        text.append(buf, amt);
+    }
+
+    close(fd);
+    
+    if (text == "") {
+        return NULL;
+    }
+        
+    ValuesFile* result = ValuesFile::ParseString(filename, text, configuration, version,
+                                                    versionString);
+    if (result == NULL) {
+        fprintf(stderr, "unable to parse file: %s\n", filename.c_str());
+        exit(1);
+    }
+    return result;
+}
+
+void
+print_file_status(size_t j, size_t J, const string& message)
+{
+    printf("\r%s file %zd of %zd...", message.c_str(), j, J);
+    fflush(stdout);
+}
+
+int
+write_to_file(const string& filename, const string& text)
+{
+    mkdirs(parent_dir(filename).c_str());
+    int fd = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
+    if (fd < 0) {
+        fprintf(stderr, "unable to open file for write (%s): %s\n", strerror(errno),
+                filename.c_str());
+        return -1;
+    }
+
+    ssize_t amt = write(fd, text.c_str(), text.length());
+
+    close(fd);
+
+    if (amt < 0) {
+        return amt;
+    }
+    return amt == (ssize_t)text.length() ? 0 : -1;
+}
+
+
diff --git a/tools/localize/file_utils.h b/tools/localize/file_utils.h
new file mode 100644
index 0000000..3b3fa21
--- /dev/null
+++ b/tools/localize/file_utils.h
@@ -0,0 +1,21 @@
+#ifndef FILE_UTILS_H
+#define FILE_UTILS_H
+
+#include "ValuesFile.h"
+#include "Configuration.h"
+#include <string>
+
+using namespace std;
+
+string translated_file_name(const string& file, const string& locale);
+
+ValuesFile* get_values_file(const string& filename, const Configuration& configuration,
+                int version, const string& versionString, bool printOnFailure);
+ValuesFile* get_local_values_file(const string& filename, const Configuration& configuration,
+                int version, const string& versionString, bool printOnFailure);
+
+void print_file_status(size_t j, size_t J, const string& message = "Reading");
+int write_to_file(const string& filename, const string& text);
+
+
+#endif // FILE_UTILS_H
diff --git a/tools/localize/localize.cpp b/tools/localize/localize.cpp
new file mode 100644
index 0000000..c0d84cc
--- /dev/null
+++ b/tools/localize/localize.cpp
@@ -0,0 +1,767 @@
+#include "SourcePos.h"
+#include "ValuesFile.h"
+#include "XLIFFFile.h"
+#include "Perforce.h"
+#include "merge_res_and_xliff.h"
+#include "localize.h"
+#include "file_utils.h"
+#include "res_check.h"
+#include "xmb.h"
+
+#include <host/pseudolocalize.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+
+using namespace std;
+
+FILE* g_logFile = NULL;
+
+int test();
+
+int
+read_settings(const string& filename, map<string,Settings>* result, const string& rootDir)
+{
+    XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY);
+    if (root == NULL) {
+        SourcePos(filename, -1).Error("Error reading file.");
+        return 1;
+    }
+
+    // <configuration>
+    vector<XMLNode*> configNodes = root->GetElementsByName("", "configuration");
+    const size_t I = configNodes.size();
+    for (size_t i=0; i<I; i++) {
+        const XMLNode* configNode = configNodes[i];
+
+        Settings settings;
+        settings.id = configNode->GetAttribute("", "id", "");
+        if (settings.id == "") {
+            configNode->Position().Error("<configuration> needs an id attribute.");
+            delete root;
+            return 1;
+        }
+
+        settings.oldVersion = configNode->GetAttribute("", "old-cl", "");
+
+        settings.currentVersion = configNode->GetAttribute("", "new-cl", "");
+        if (settings.currentVersion == "") {
+            configNode->Position().Error("<configuration> needs a new-cl attribute.");
+            delete root;
+            return 1;
+        }
+
+        // <app>
+        vector<XMLNode*> appNodes = configNode->GetElementsByName("", "app");
+
+        const size_t J = appNodes.size();
+        for (size_t j=0; j<J; j++) {
+            const XMLNode* appNode = appNodes[j];
+
+            string dir = appNode->GetAttribute("", "dir", "");
+            if (dir == "") {
+                appNode->Position().Error("<app> needs a dir attribute.");
+                delete root;
+                return 1;
+            }
+
+            settings.apps.push_back(dir);
+        }
+
+        // <reject>
+        vector<XMLNode*> rejectNodes = configNode->GetElementsByName("", "reject");
+
+        const size_t K = rejectNodes.size();
+        for (size_t k=0; k<K; k++) {
+            const XMLNode* rejectNode = rejectNodes[k];
+
+            Reject reject;
+
+            reject.file = rejectNode->GetAttribute("", "file", "");
+            if (reject.file == "") {
+                rejectNode->Position().Error("<reject> needs a file attribute.");
+                delete root;
+                return 1;
+            }
+            string f =  reject.file;
+            reject.file = rootDir;
+            reject.file += '/';
+            reject.file += f;
+            
+            reject.name = rejectNode->GetAttribute("", "name", "");
+            if (reject.name == "") {
+                rejectNode->Position().Error("<reject> needs a name attribute.");
+                delete root;
+                return 1;
+            }
+
+            reject.comment = trim_string(rejectNode->CollapseTextContents());
+
+            settings.reject.push_back(reject);
+        }
+
+        (*result)[settings.id] = settings;
+    }
+
+    delete root;
+    return 0;
+}
+
+
+static void
+ValuesFile_to_XLIFFFile(const ValuesFile* values, XLIFFFile* xliff, const string& englishFilename)
+{
+    const set<StringResource>& strings = values->GetStrings();
+    for (set<StringResource>::const_iterator it=strings.begin(); it!=strings.end(); it++) {
+        StringResource res = *it;
+        res.file = englishFilename;
+        xliff->AddStringResource(res);
+    }
+}
+
+static bool
+contains_reject(const Settings& settings, const string& file, const TransUnit& tu)
+{
+    const string name = tu.id;
+    const vector<Reject>& reject = settings.reject;
+    const size_t I = reject.size();
+    for (size_t i=0; i<I; i++) {
+        const Reject& r = reject[i];
+        if (r.file == file && r.name == name) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * If it's been rejected, then we keep whatever info we have.
+ *
+ * Implements this truth table:
+ *
+ *    S   AT   AS     Keep
+ *   -----------------------
+ *    0    0    0      0    (this case can't happen)
+ *    0    0    1      0    (it was there, never translated, and removed)
+ *    0    1    0      0    (somehow it got translated, but it was removed)
+ *    0    1    1      0    (it was removed after having been translated)
+ *
+ *    1    0    0      1    (it was just added)
+ *    1    0    1      1    (it was added, has been changed, but it never got translated)
+ *    1    1    0      1    (somehow it got translated, but we don't know based on what)
+ *    1    1    1     0/1   (it's in both.  0 if S=AS b/c there's no need to retranslate if they're
+ *                           the same.  1 if S!=AS because S changed, so it should be retranslated)
+ *
+ * The first four are cases where, whatever happened in the past, the string isn't there
+ * now, so it shouldn't be in the XLIFF file.
+ *
+ * For cases 4 and 5, the string has never been translated, so get it translated.
+ *
+ * For case 6, it's unclear where the translated version came from, so we're conservative
+ * and send it back for them to have another shot at.
+ *
+ * For case 7, we have some data.  We have two choices.  We could rely on the translator's
+ * translation memory or tools to notice that the strings haven't changed, and populate the
+ * <target> field themselves.  Or if the string hasn't changed since last time, we can just
+ * not even tell them about it.  As the project nears the end, it will be convenient to see
+ * the xliff files reducing in size, so we pick the latter.  Obviously, if the string has
+ * changed, then we need to get it retranslated.
+ */
+bool
+keep_this_trans_unit(const string& file, const TransUnit& unit, void* cookie)
+{
+    const Settings* settings = reinterpret_cast<const Settings*>(cookie);
+
+    if (contains_reject(*settings, file, unit)) {
+        return true;
+    }
+
+    if (unit.source.id == "") {
+        return false;
+    }
+    if (unit.altTarget.id == "" || unit.altSource.id == "") {
+        return true;
+    }
+    return unit.source.value->ContentsToString(XLIFF_NAMESPACES)
+            != unit.altSource.value->ContentsToString(XLIFF_NAMESPACES);
+}
+
+int
+validate_config(const string& settingsFile, const map<string,Settings>& settings,
+        const string& config)
+{
+    if (settings.find(config) == settings.end()) {
+        SourcePos(settingsFile, -1).Error("settings file does not contain setting: %s\n",
+                config.c_str());
+        return 1;
+    }
+    return 0;
+}
+
+int
+validate_configs(const string& settingsFile, const map<string,Settings>& settings,
+        const vector<string>& configs)
+{
+    int err = 0;
+    for (size_t i=0; i<configs.size(); i++) {
+        string config = configs[i];
+        err |= validate_config(settingsFile, settings, config);
+    }
+    return err;
+}
+
+int
+select_files(vector<string> *resFiles, const string& config,
+        const map<string,Settings>& settings, const string& rootDir)
+{
+    int err;
+    vector<vector<string> > allResFiles;
+    vector<string> configs;
+    configs.push_back(config);
+    err = select_files(&allResFiles, configs, settings, rootDir);
+    if (err == 0) {
+        *resFiles = allResFiles[0];
+    }
+    return err;
+}
+
+int
+select_files(vector<vector<string> > *allResFiles, const vector<string>& configs,
+        const map<string,Settings>& settings, const string& rootDir)
+{
+    int err;
+    printf("Selecting files...");
+    fflush(stdout);
+
+    for (size_t i=0; i<configs.size(); i++) {
+        const string& config = configs[i];
+        const Settings& setting = settings.find(config)->second;
+
+        vector<string> resFiles;
+        err = Perforce::GetResourceFileNames(setting.currentVersion, rootDir,
+                                                setting.apps, &resFiles, true);
+        if (err != 0) {
+            fprintf(stderr, "error with perforce.  bailing\n");
+            return err;
+        }
+
+        allResFiles->push_back(resFiles);
+    }
+    return 0;
+}
+
+static int
+do_export(const string& settingsFile, const string& rootDir, const string& outDir,
+            const string& targetLocale, const vector<string>& configs)
+{
+    bool success = true;
+    int err;
+
+    if (false) {
+        printf("settingsFile=%s\n", settingsFile.c_str());
+        printf("rootDir=%s\n", rootDir.c_str());
+        printf("outDir=%s\n", outDir.c_str());
+        for (size_t i=0; i<configs.size(); i++) {
+            printf("config[%zd]=%s\n", i, configs[i].c_str());
+        }
+    }
+
+    map<string,Settings> settings;
+    err = read_settings(settingsFile, &settings, rootDir);
+    if (err != 0) {
+        return err;
+    }
+
+    err = validate_configs(settingsFile, settings, configs);
+    if (err != 0) {
+        return err;
+    }
+
+    vector<vector<string> > allResFiles;
+    err = select_files(&allResFiles, configs, settings, rootDir);
+    if (err != 0) {
+        return err;
+    }
+
+    size_t totalFileCount = 0;
+    for (size_t i=0; i<allResFiles.size(); i++) {
+        totalFileCount += allResFiles[i].size();
+    }
+    totalFileCount *= 3; // we try all 3 versions of the file
+
+    size_t fileProgress = 0;
+    vector<Stats> stats;
+    vector<pair<string,XLIFFFile*> > xliffs;
+
+    for (size_t i=0; i<configs.size(); i++) {
+        const string& config = configs[i];
+        const Settings& setting = settings[config];
+
+        if (false) {
+            fprintf(stderr, "Configuration: %s (%zd of %zd)\n", config.c_str(), i+1,
+                    configs.size());
+            fprintf(stderr, "  Old CL:     %s\n", setting.oldVersion.c_str());
+            fprintf(stderr, "  Current CL: %s\n", setting.currentVersion.c_str());
+        }
+
+        Configuration english;
+            english.locale = "en_US";
+        Configuration translated;
+            translated.locale = targetLocale;
+        XLIFFFile* xliff = XLIFFFile::Create(english, translated, setting.currentVersion);
+
+        const vector<string>& resFiles = allResFiles[i];
+        const size_t J = resFiles.size();
+        for (size_t j=0; j<J; j++) {
+            string resFile = resFiles[j];
+
+            // parse the files into a ValuesFile
+            // pull out the strings and add them to the XLIFFFile
+            
+            // current file
+            print_file_status(++fileProgress, totalFileCount);
+            ValuesFile* currentFile = get_values_file(resFile, english, CURRENT_VERSION,
+                                                        setting.currentVersion, true);
+            if (currentFile != NULL) {
+                ValuesFile_to_XLIFFFile(currentFile, xliff, resFile);
+                //printf("currentFile=[%s]\n", currentFile->ToString().c_str());
+            } else {
+                fprintf(stderr, "error reading file %s@%s\n", resFile.c_str(),
+                            setting.currentVersion.c_str());
+                success = false;
+            }
+
+            // old file
+            print_file_status(++fileProgress, totalFileCount);
+            ValuesFile* oldFile = get_values_file(resFile, english, OLD_VERSION,
+                                                        setting.oldVersion, false);
+            if (oldFile != NULL) {
+                ValuesFile_to_XLIFFFile(oldFile, xliff, resFile);
+                //printf("oldFile=[%s]\n", oldFile->ToString().c_str());
+            }
+
+            // translated version
+            // (get the head of the tree for the most recent translation, but it's considered
+            // the old one because the "current" one hasn't been made yet, and this goes into
+            // the <alt-trans> tag if necessary
+            print_file_status(++fileProgress, totalFileCount);
+            string transFilename = translated_file_name(resFile, targetLocale);
+            ValuesFile* transFile = get_values_file(transFilename, translated, OLD_VERSION,
+                                                        setting.currentVersion, false);
+            if (transFile != NULL) {
+                ValuesFile_to_XLIFFFile(transFile, xliff, resFile);
+            }
+
+            delete currentFile;
+            delete oldFile;
+            delete transFile;
+        }
+
+        Stats beforeFilterStats = xliff->GetStats(config);
+
+        // run through the XLIFFFile and strip out TransUnits that have identical
+        // old and current source values and are not in the reject list, or just
+        // old values and no source values
+        xliff->Filter(keep_this_trans_unit, (void*)&setting);
+
+        Stats afterFilterStats = xliff->GetStats(config);
+        afterFilterStats.totalStrings = beforeFilterStats.totalStrings;
+
+        // add the reject comments
+        for (vector<Reject>::const_iterator reject = setting.reject.begin();
+                reject != setting.reject.end(); reject++) {
+            TransUnit* tu = xliff->EditTransUnit(reject->file, reject->name);
+            tu->rejectComment = reject->comment;
+        }
+
+        // config-locale-current_cl.xliff
+        stringstream filename;
+        if (outDir != "") {
+            filename << outDir << '/';
+        }
+        filename << config << '-' << targetLocale << '-' << setting.currentVersion << ".xliff";
+        xliffs.push_back(pair<string,XLIFFFile*>(filename.str(), xliff));
+
+        stats.push_back(afterFilterStats);
+    }
+
+    // today is a good day to die
+    if (!success || SourcePos::HasErrors()) {
+        return 1;
+    }
+
+    // write the XLIFF files
+    printf("\nWriting %zd file%s...\n", xliffs.size(), xliffs.size() == 1 ? "" : "s");
+    for (vector<pair<string,XLIFFFile*> >::iterator it = xliffs.begin(); it != xliffs.end(); it++) {
+        const string& filename = it->first;
+        XLIFFFile* xliff = it->second;
+        string text = xliff->ToString();
+        write_to_file(filename, text);
+    }
+
+    // the stats
+    printf("\n"
+           "                                  to          without     total\n"
+           " config               files       translate   comments    strings\n"
+           "-----------------------------------------------------------------------\n");
+    Stats totals;
+        totals.config = "total";
+        totals.files = 0;
+        totals.toBeTranslated = 0;
+        totals.noComments = 0;
+        totals.totalStrings = 0;
+    for (vector<Stats>::iterator it=stats.begin(); it!=stats.end(); it++) {
+        string cfg = it->config;
+        if (cfg.length() > 20) {
+            cfg.resize(20);
+        }
+        printf(" %-20s  %-9zd   %-9zd   %-9zd   %-19zd\n", cfg.c_str(), it->files,
+                it->toBeTranslated, it->noComments, it->totalStrings);
+        totals.files += it->files;
+        totals.toBeTranslated += it->toBeTranslated;
+        totals.noComments += it->noComments;
+        totals.totalStrings += it->totalStrings;
+    }
+    if (stats.size() > 1) {
+        printf("-----------------------------------------------------------------------\n"
+               " %-20s  %-9zd   %-9zd   %-9zd   %-19zd\n", totals.config.c_str(), totals.files,
+                    totals.toBeTranslated, totals.noComments, totals.totalStrings);
+    }
+    printf("\n");
+    return 0;
+}
+
+struct PseudolocalizeSettings {
+    XLIFFFile* xliff;
+    bool expand;
+};
+
+
+string
+pseudolocalize_string(const string& source, const PseudolocalizeSettings* settings)
+{
+    return pseudolocalize_string(source);
+}
+
+static XMLNode*
+pseudolocalize_xml_node(const XMLNode* source, const PseudolocalizeSettings* settings)
+{
+    if (source->Type() == XMLNode::TEXT) {
+        return XMLNode::NewText(source->Position(), pseudolocalize_string(source->Text(), settings),
+                                source->Pretty());
+    } else {
+        XMLNode* target;
+        if (source->Namespace() == XLIFF_XMLNS && source->Name() == "g") {
+            // XXX don't translate these
+            target = XMLNode::NewElement(source->Position(), source->Namespace(),
+                                    source->Name(), source->Attributes(), source->Pretty());
+        } else {
+            target = XMLNode::NewElement(source->Position(), source->Namespace(),
+                                    source->Name(), source->Attributes(), source->Pretty());
+        }
+
+        const vector<XMLNode*>& children = source->Children();
+        const size_t I = children.size();
+        for (size_t i=0; i<I; i++) {
+            target->EditChildren().push_back(pseudolocalize_xml_node(children[i], settings));
+        }
+
+        return target;
+    }
+}
+
+void
+pseudolocalize_trans_unit(const string&file, TransUnit* unit, void* cookie)
+{
+    const PseudolocalizeSettings* settings = (PseudolocalizeSettings*)cookie;
+
+    const StringResource& source = unit->source;
+    StringResource* target = &unit->target;
+    *target = source;
+
+    target->config = settings->xliff->TargetConfig();
+
+    delete target->value;
+    target->value = pseudolocalize_xml_node(source.value, settings);
+}
+
+int
+pseudolocalize_xliff(XLIFFFile* xliff, bool expand)
+{
+    PseudolocalizeSettings settings;
+
+    settings.xliff = xliff;
+    settings.expand = expand;
+    xliff->Map(pseudolocalize_trans_unit, &settings);
+    return 0;
+}
+
+static int
+do_pseudo(const string& infile, const string& outfile, bool expand)
+{
+    int err;
+
+    XLIFFFile* xliff = XLIFFFile::Parse(infile);
+    if (xliff == NULL) {
+        return 1;
+    }
+
+    pseudolocalize_xliff(xliff, expand);
+
+    err = write_to_file(outfile, xliff->ToString());
+
+    delete xliff;
+
+    return err;
+}
+
+void
+log_printf(const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    if (g_logFile != NULL) {
+        va_start(ap, fmt);
+        ret = vfprintf(g_logFile, fmt, ap);
+        va_end(ap);
+        fflush(g_logFile);
+    }
+}
+
+void
+close_log_file()
+{
+    if (g_logFile != NULL) {
+        fclose(g_logFile);
+    }
+}
+
+void
+open_log_file(const char* file)
+{
+    g_logFile = fopen(file, "w");
+    printf("log file: %s -- %p\n", file, g_logFile);
+    atexit(close_log_file);
+}
+
+static int
+usage()
+{
+    fprintf(stderr,
+            "usage: localize export OPTIONS CONFIGS...\n"
+            "   REQUIRED OPTIONS\n"
+            "     --settings SETTINGS   The settings file to use.  See CONFIGS below.\n"
+            "     --root TREE_ROOT      The location in Perforce of the files.  e.g. //device\n"
+            "     --target LOCALE       The target locale.  See LOCALES below.\n"
+            "\n"
+            "   OPTIONAL OPTIONS\n"
+            "      --out DIR            Directory to put the output files.  Defaults to the\n"
+            "                           current directory if not supplied.  Files are\n"
+            "                           named as follows:\n"
+            "                               CONFIG-LOCALE-CURRENT_CL.xliff\n"
+            "\n"
+            "\n"
+            "usage: localize import XLIFF_FILE...\n"
+            "\n"
+            "Import a translated XLIFF file back into the tree.\n"
+            "\n"
+            "\n"
+            "usage: localize xlb XMB_FILE VALUES_FILES...\n"
+            "\n"
+            "Read resource files from the tree file and write the corresponding XLB file\n"
+            "\n"
+            "Supply all of the android resource files (values files) to export after that.\n"
+            "\n"
+            "\n"
+            "\n"
+            "CONFIGS\n"
+            "\n"
+            "LOCALES\n"
+            "Locales are specified in the form en_US  They will be processed correctly\n"
+            "to locate the resouce files in the tree.\n"
+            "\n"
+            "\n"
+            "usage: localize pseudo OPTIONS INFILE [OUTFILE]\n"
+            "   OPTIONAL OPTIONS\n"
+            "     --big                 Pad strings so they get longer.\n"
+            "\n"
+            "Read INFILE, an XLIFF file, and output a pseudotranslated version of that file.  If\n"
+            "OUTFILE is specified, the results are written there; otherwise, the results are\n"
+            "written back to INFILE.\n"
+            "\n"
+            "\n"
+            "usage: localize rescheck FILES...\n"
+            "\n"
+            "Reads the base strings and prints warnings about bad resources from the given files.\n"
+            "\n");
+    return 1;
+}
+
+int
+main(int argc, const char** argv)
+{
+    //open_log_file("log.txt");
+    //g_logFile = stdout;
+
+    if (argc == 2 && 0 == strcmp(argv[1], "--test")) {
+        return test();
+    }
+
+    if (argc < 2) {
+        return usage();
+    }
+
+    int index = 1;
+    
+    if (0 == strcmp("export", argv[index])) {
+        string settingsFile;
+        string rootDir;
+        string outDir;
+        string baseLocale = "en";
+        string targetLocale;
+        string language, region;
+        vector<string> configs;
+
+        index++;
+        while (index < argc) {
+            if (0 == strcmp("--settings", argv[index])) {
+                settingsFile = argv[index+1];
+                index += 2;
+            }
+            else if (0 == strcmp("--root", argv[index])) {
+                rootDir = argv[index+1];
+                index += 2;
+            }
+            else if (0 == strcmp("--out", argv[index])) {
+                outDir = argv[index+1];
+                index += 2;
+            }
+            else if (0 == strcmp("--target", argv[index])) {
+                targetLocale = argv[index+1];
+                index += 2;
+            }
+            else if (argv[index][0] == '-') {
+                fprintf(stderr, "unknown argument %s\n", argv[index]);
+                return usage();
+            }
+            else {
+                break;
+            }
+        }
+        for (; index<argc; index++) {
+            configs.push_back(argv[index]);
+        }
+
+        if (settingsFile == "" || rootDir == "" || configs.size() == 0 || targetLocale == "") {
+            return usage();
+        }
+        if (!split_locale(targetLocale, &language, &region)) {
+            fprintf(stderr, "illegal --target locale: '%s'\n", targetLocale.c_str());
+            return usage();
+        }
+
+
+        return do_export(settingsFile, rootDir, outDir, targetLocale, configs);
+    }
+    else if (0 == strcmp("import", argv[index])) {
+        vector<string> xliffFilenames;
+
+        index++;
+        for (; index<argc; index++) {
+            xliffFilenames.push_back(argv[index]);
+        }
+
+        return do_merge(xliffFilenames);
+    }
+    else if (0 == strcmp("xlb", argv[index])) {
+        string outfile;
+        vector<string> resFiles;
+
+        index++;
+        if (argc < index+1) {
+            return usage();
+        }
+
+        outfile = argv[index];
+
+        index++;
+        for (; index<argc; index++) {
+            resFiles.push_back(argv[index]);
+        }
+
+        return do_xlb_export(outfile, resFiles);
+    }
+    else if (0 == strcmp("pseudo", argv[index])) {
+        string infile;
+        string outfile;
+        bool big = false;
+
+        index++;
+        while (index < argc) {
+            if (0 == strcmp("--big", argv[index])) {
+                big = true;
+                index += 1;
+            }
+            else if (argv[index][0] == '-') {
+                fprintf(stderr, "unknown argument %s\n", argv[index]);
+                return usage();
+            }
+            else {
+                break;
+            }
+        }
+
+        if (index == argc-1) {
+            infile = argv[index];
+            outfile = argv[index];
+        }
+        else if (index == argc-2) {
+            infile = argv[index];
+            outfile = argv[index+1];
+        }
+        else {
+            fprintf(stderr, "unknown argument %s\n", argv[index]);
+            return usage();
+        }
+
+        return do_pseudo(infile, outfile, big);
+    }
+    else if (0 == strcmp("rescheck", argv[index])) {
+        vector<string> files;
+
+        index++;
+        while (index < argc) {
+            if (argv[index][0] == '-') {
+                fprintf(stderr, "unknown argument %s\n", argv[index]);
+                return usage();
+            }
+            else {
+                break;
+            }
+        }
+        for (; index<argc; index++) {
+            files.push_back(argv[index]);
+        }
+
+        if (files.size() == 0) {
+            return usage();
+        }
+
+        return do_rescheck(files);
+    }
+    else {
+        return usage();
+    }
+
+    if (SourcePos::HasErrors()) {
+        SourcePos::PrintErrors(stderr);
+        return 1;
+    }
+
+    return 0;
+}
+
diff --git a/tools/localize/localize.h b/tools/localize/localize.h
new file mode 100644
index 0000000..615d14e
--- /dev/null
+++ b/tools/localize/localize.h
@@ -0,0 +1,40 @@
+#ifndef LOCALIZE_H
+#define LOCALIZE_H
+
+#include "XLIFFFile.h"
+
+#include <map>
+#include <string>
+
+using namespace std;
+
+struct Reject
+{
+    string file;
+    string name;
+    string comment;
+};
+
+struct Settings
+{
+    string id;
+    string oldVersion;
+    string currentVersion;
+    vector<string> apps;
+    vector<Reject> reject;
+};
+
+int read_settings(const string& filename, map<string,Settings>* result, const string& rootDir);
+string translated_file_name(const string& file, const string& locale);
+bool keep_this_trans_unit(const string& file, const TransUnit& unit, void* cookie);
+int validate_config(const string& settingsFile, const map<string,Settings>& settings,
+        const string& configs);
+int validate_configs(const string& settingsFile, const map<string,Settings>& settings,
+        const vector<string>& configs);
+int select_files(vector<string> *resFiles, const string& config,
+        const map<string,Settings>& settings, const string& rootDir);
+int select_files(vector<vector<string> > *allResFiles, const vector<string>& configs,
+        const map<string,Settings>& settings, const string& rootDir);
+
+
+#endif // LOCALIZE_H
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
new file mode 100644
index 0000000..63d904c
--- /dev/null
+++ b/tools/localize/localize_test.cpp
@@ -0,0 +1,219 @@
+#include "XLIFFFile.h"
+#include "ValuesFile.h"
+#include "localize.h"
+
+int pseudolocalize_xliff(XLIFFFile* xliff, bool expand);
+
+static int
+test_filename(const string& file, const string& locale, const string& expected)
+{
+    string result = translated_file_name(file, locale);
+    if (result != expected) {
+        fprintf(stderr, "translated_file_name test failed\n");
+        fprintf(stderr, "  locale='%s'\n", locale.c_str());
+        fprintf(stderr, "  expected='%s'\n", expected.c_str());
+        fprintf(stderr, "    result='%s'\n", result.c_str());
+        return 1;
+    } else {
+        if (false) {
+            fprintf(stderr, "translated_file_name test passed\n");
+            fprintf(stderr, "  locale='%s'\n", locale.c_str());
+            fprintf(stderr, "  expected='%s'\n", expected.c_str());
+            fprintf(stderr, "    result='%s'\n", result.c_str());
+        }
+        return 0;
+    }
+}
+
+static int
+translated_file_name_test()
+{
+    bool all = true;
+    int err = 0;
+
+    if (all) err |= test_filename("//device/samples/NotePad/res/values/strings.xml", "zz_ZZ",
+                                  "//device/samples/NotePad/res/values-zz-rZZ/strings.xml");
+
+    if (all) err |= test_filename("//device/samples/NotePad/res/values/strings.xml", "zz",
+                                  "//device/samples/NotePad/res/values-zz/strings.xml");
+
+    if (all) err |= test_filename("//device/samples/NotePad/res/values/strings.xml", "",
+                                  "//device/samples/NotePad/res/values/strings.xml");
+
+    return err;
+}
+
+bool
+return_false(const string&, const TransUnit& unit, void* cookie)
+{
+    return false;
+}
+
+static int
+delete_trans_units()
+{
+    XLIFFFile* xliff = XLIFFFile::Parse("testdata/strip_xliff.xliff");
+    if (xliff == NULL) {
+        printf("couldn't read file\n");
+        return 1;
+    }
+    if (false) {
+        printf("XLIFF was [[%s]]\n", xliff->ToString().c_str());
+    }
+
+    xliff->Filter(return_false, NULL);
+
+    if (false) {
+        printf("XLIFF is [[%s]]\n", xliff->ToString().c_str());
+
+        set<StringResource> const& strings = xliff->GetStringResources();
+        printf("strings.size=%zd\n", strings.size());
+        for (set<StringResource>::iterator it=strings.begin(); it!=strings.end(); it++) {
+            const StringResource& str = *it;
+            printf("STRING!!! id=%s value='%s' pos=%s file=%s version=%d(%s)\n", str.id.c_str(),
+                    str.value->ContentsToString(ANDROID_NAMESPACES).c_str(),
+                    str.pos.ToString().c_str(), str.file.c_str(), str.version,
+                    str.versionString.c_str());
+        }
+    }
+ 
+    return 0;
+}
+
+static int
+filter_trans_units()
+{
+    XLIFFFile* xliff = XLIFFFile::Parse("testdata/strip_xliff.xliff");
+    if (xliff == NULL) {
+        printf("couldn't read file\n");
+        return 1;
+    }
+
+    if (false) {
+        printf("XLIFF was [[%s]]\n", xliff->ToString().c_str());
+    }
+
+    Settings setting;
+    xliff->Filter(keep_this_trans_unit, &setting);
+
+    if (false) {
+        printf("XLIFF is [[%s]]\n", xliff->ToString().c_str());
+
+        set<StringResource> const& strings = xliff->GetStringResources();
+        printf("strings.size=%zd\n", strings.size());
+        for (set<StringResource>::iterator it=strings.begin(); it!=strings.end(); it++) {
+            const StringResource& str = *it;
+            printf("STRING!!! id=%s value='%s' pos=%s file=%s version=%d(%s)\n", str.id.c_str(),
+                    str.value->ContentsToString(ANDROID_NAMESPACES).c_str(),
+                    str.pos.ToString().c_str(), str.file.c_str(), str.version,
+                    str.versionString.c_str());
+        }
+    }
+ 
+    return 0;
+}
+
+static int
+settings_test()
+{
+    int err;
+    map<string,Settings> settings;
+    map<string,Settings>::iterator it;
+
+    err = read_settings("testdata/config.xml", &settings, "//asdf");
+    if (err != 0) {
+        return err;
+    }
+
+    if (false) {
+        for (it=settings.begin(); it!=settings.end(); it++) {
+            const Settings& setting = it->second;
+            printf("CONFIG:\n");
+            printf("              id='%s'\n", setting.id.c_str());
+            printf("      oldVersion='%s'\n", setting.oldVersion.c_str());
+            printf("  currentVersion='%s'\n", setting.currentVersion.c_str());
+            int i=0;
+            for (vector<string>::const_iterator app=setting.apps.begin();
+                    app!=setting.apps.end(); app++) {
+                printf("        apps[%02d]='%s'\n", i, app->c_str());
+                i++;
+            }
+            i=0;
+            for (vector<Reject>::const_iterator reject=setting.reject.begin();
+                    reject!=setting.reject.end(); reject++) {
+                i++;
+                printf("      reject[%02d]=('%s','%s','%s')\n", i, reject->file.c_str(),
+                        reject->name.c_str(), reject->comment.c_str());
+            }
+        }
+    }
+
+    for (it=settings.begin(); it!=settings.end(); it++) {
+        const Settings& setting = it->second;
+        if (it->first != setting.id) {
+            fprintf(stderr, "it->first='%s' setting.id='%s'\n", it->first.c_str(),
+                    setting.id.c_str());
+            err |= 1;
+        }
+    }
+
+
+    return err;
+}
+
+static int
+test_one_pseudo(bool big, const char* expected)
+{
+    XLIFFFile* xliff = XLIFFFile::Parse("testdata/pseudo.xliff");
+    if (xliff == NULL) {
+        printf("couldn't read file\n");
+        return 1;
+    }
+    if (false) {
+        printf("XLIFF was [[%s]]\n", xliff->ToString().c_str());
+    }
+
+    pseudolocalize_xliff(xliff, big);
+    string newString = xliff->ToString();
+    delete xliff;
+
+    if (false) {
+        printf("XLIFF is [[%s]]\n", newString.c_str());
+    }
+
+    if (false && newString != expected) {
+        fprintf(stderr, "xliff didn't translate as expected\n");
+        fprintf(stderr, "newString=[[%s]]\n", newString.c_str());
+        fprintf(stderr, "expected=[[%s]]\n", expected);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+pseudolocalize_test()
+{
+    int err = 0;
+    
+    err |= test_one_pseudo(false, "");
+    //err |= test_one_pseudo(true, "");
+
+    return err;
+}
+
+int
+localize_test()
+{
+    bool all = true;
+    int err = 0;
+
+    if (all) err |= translated_file_name_test();
+    if (all) err |= delete_trans_units();
+    if (all) err |= filter_trans_units();
+    if (all) err |= settings_test();
+    if (all) err |= pseudolocalize_test();
+
+    return err;
+}
+
diff --git a/tools/localize/log.h b/tools/localize/log.h
new file mode 100644
index 0000000..4a5fa7f
--- /dev/null
+++ b/tools/localize/log.h
@@ -0,0 +1,7 @@
+#ifndef LOG_H
+#define LOG_H
+
+void log_printf(const char* fmt, ...);
+
+#endif // LOG_H
+
diff --git a/tools/localize/merge_res_and_xliff.cpp b/tools/localize/merge_res_and_xliff.cpp
new file mode 100644
index 0000000..58a6554
--- /dev/null
+++ b/tools/localize/merge_res_and_xliff.cpp
@@ -0,0 +1,391 @@
+#include "merge_res_and_xliff.h"
+
+#include "file_utils.h"
+#include "Perforce.h"
+#include "log.h"
+
+static set<StringResource>::const_iterator
+find_id(const set<StringResource>& s, const string& id, int index)
+{
+    for (set<StringResource>::const_iterator it = s.begin(); it != s.end(); it++) {
+        if (it->id == id && it->index == index) {
+            return it;
+        }
+    }
+    return s.end();
+}
+
+static set<StringResource>::const_iterator
+find_in_xliff(const set<StringResource>& s, const string& filename, const string& id, int index,
+                int version, const Configuration& config)
+{
+    for (set<StringResource>::const_iterator it = s.begin(); it != s.end(); it++) {
+        if (it->file == filename && it->id == id && it->index == index && it->version == version
+                && it->config == config) {
+            return it;
+        }
+    }
+    return s.end();
+}
+
+
+static void
+printit(const set<StringResource>& s, const set<StringResource>::const_iterator& it)
+{
+    if (it == s.end()) {
+        printf("(none)\n");
+    } else {
+        printf("id=%s index=%d config=%s file=%s value='%s'\n", it->id.c_str(), it->index,
+                it->config.ToString().c_str(), it->file.c_str(),
+                it->value->ToString(ANDROID_NAMESPACES).c_str());
+    }
+}
+
+StringResource
+convert_resource(const StringResource& s, const string& file, const Configuration& config,
+                    int version, const string& versionString)
+{
+    return StringResource(s.pos, file, config, s.id, s.index, s.value ? s.value->Clone() : NULL,
+            version, versionString, s.comment);
+}
+
+static bool
+resource_has_contents(const StringResource& res)
+{
+    XMLNode* value = res.value;
+    if (value == NULL) {
+        return false;
+    }
+    string contents = value->ContentsToString(ANDROID_NAMESPACES);
+    return contents != "";
+}
+
+ValuesFile*
+merge_res_and_xliff(const ValuesFile* en_currentFile,
+        const ValuesFile* xx_currentFile, const ValuesFile* xx_oldFile,
+        const string& filename, const XLIFFFile* xliffFile)
+{
+    bool success = true;
+
+    Configuration en_config = xliffFile->SourceConfig();
+    Configuration xx_config = xliffFile->TargetConfig();
+    string currentVersion = xliffFile->CurrentVersion();
+
+    ValuesFile* result = new ValuesFile(xx_config);
+
+    set<StringResource> en_cur = en_currentFile->GetStrings();
+    set<StringResource> xx_cur = xx_currentFile->GetStrings();
+    set<StringResource> xx_old = xx_oldFile->GetStrings();
+    set<StringResource> xliff = xliffFile->GetStringResources();
+
+    // for each string in en_current
+    for (set<StringResource>::const_iterator en_c = en_cur.begin();
+            en_c != en_cur.end(); en_c++) {
+        set<StringResource>::const_iterator xx_c = find_id(xx_cur, en_c->id, en_c->index);
+        set<StringResource>::const_iterator xx_o = find_id(xx_old, en_c->id, en_c->index);
+        set<StringResource>::const_iterator xlf = find_in_xliff(xliff, en_c->file, en_c->id,
+                                                        en_c->index, CURRENT_VERSION, xx_config);
+
+        if (false) {
+            printf("\nen_c: "); printit(en_cur, en_c);
+            printf("xx_c: "); printit(xx_cur, xx_c);
+            printf("xx_o: "); printit(xx_old, xx_o);
+            printf("xlf:  "); printit(xliff, xlf);
+        }
+
+        // if it changed between xx_old and xx_current, use xx_current
+        // (someone changed it by hand)
+        if (xx_o != xx_old.end() && xx_c != xx_cur.end()) {
+            string xx_o_value = xx_o->value->ToString(ANDROID_NAMESPACES);
+            string xx_c_value = xx_c->value->ToString(ANDROID_NAMESPACES);
+            if (xx_o_value != xx_c_value && xx_c_value != "") {
+                StringResource r(convert_resource(*xx_c, filename, xx_config,
+                                                    CURRENT_VERSION, currentVersion));
+                if (resource_has_contents(r)) {
+                    result->AddString(r);
+                }
+                continue;
+            }
+        }
+
+        // if it is present in xliff, use that
+        // (it just got translated)
+        if (xlf != xliff.end() && xlf->value->ToString(ANDROID_NAMESPACES) != "") {
+            StringResource r(convert_resource(*xlf, filename, xx_config,
+                                                CURRENT_VERSION, currentVersion));
+            if (resource_has_contents(r)) {
+                result->AddString(r);
+            }
+        }
+
+        // if it is present in xx_current, use that
+        // (it was already translated, and not retranslated)
+        // don't filter out empty strings if they were added by hand, the above code just
+        // guarantees that this tool never adds an empty one.
+        if (xx_c != xx_cur.end()) {
+            StringResource r(convert_resource(*xx_c, filename, xx_config,
+                                                CURRENT_VERSION, currentVersion));
+            result->AddString(r);
+        }
+
+        // othwerwise, leave it out.  The resource fall-through code will use the English
+        // one at runtime, and the xliff export code will pick it up for translation next time.
+    }
+
+    if (success) {
+        return result;
+    } else {
+        delete result;
+        return NULL;
+    }
+}
+
+
+struct MergedFile {
+    XLIFFFile* xliff;
+    string xliffFilename;
+    string original;
+    string translated;
+    ValuesFile* en_current;
+    ValuesFile* xx_current;
+    ValuesFile* xx_old;
+    ValuesFile* xx_new;
+    string xx_new_text;
+    string xx_new_filename;
+    bool new_file;
+    bool deleted_file;
+
+    MergedFile();
+    MergedFile(const MergedFile&);
+};
+
+struct compare_filenames {
+    bool operator()(const MergedFile& lhs, const MergedFile& rhs) const
+    {
+        return lhs.original < rhs.original;
+    }
+};
+
+MergedFile::MergedFile()
+    :xliff(NULL),
+     xliffFilename(),
+     original(),
+     translated(),
+     en_current(NULL),
+     xx_current(NULL),
+     xx_old(NULL),
+     xx_new(NULL),
+     xx_new_text(),
+     xx_new_filename(),
+     new_file(false),
+     deleted_file(false)
+{
+}
+
+MergedFile::MergedFile(const MergedFile& that)
+    :xliff(that.xliff),
+     xliffFilename(that.xliffFilename),
+     original(that.original),
+     translated(that.translated),
+     en_current(that.en_current),
+     xx_current(that.xx_current),
+     xx_old(that.xx_old),
+     xx_new(that.xx_new),
+     xx_new_text(that.xx_new_text),
+     xx_new_filename(that.xx_new_filename),
+     new_file(that.new_file),
+     deleted_file(that.deleted_file)
+{
+}
+
+
+typedef set<MergedFile, compare_filenames> MergedFileSet;
+
+int
+do_merge(const vector<string>& xliffFilenames)
+{
+    int err = 0;
+    MergedFileSet files;
+
+    printf("\rPreparing..."); fflush(stdout);
+    string currentChange = Perforce::GetCurrentChange(true);
+
+    // for each xliff, make a MergedFile record and do a little error checking
+    for (vector<string>::const_iterator xliffFilename=xliffFilenames.begin();
+            xliffFilename!=xliffFilenames.end(); xliffFilename++) {
+        XLIFFFile* xliff = XLIFFFile::Parse(*xliffFilename);
+        if (xliff == NULL) {
+            fprintf(stderr, "localize import: unable to read file %s\n", xliffFilename->c_str());
+            err = 1;
+            continue;
+        }
+
+        set<string> xf = xliff->Files();
+        for (set<string>::const_iterator f=xf.begin(); f!=xf.end(); f++) {
+            MergedFile mf;
+            mf.xliff = xliff;
+            mf.xliffFilename = *xliffFilename;
+            mf.original = *f;
+            mf.translated = translated_file_name(mf.original, xliff->TargetConfig().locale);
+            log_printf("mf.translated=%s mf.original=%s locale=%s\n", mf.translated.c_str(),
+                    mf.original.c_str(), xliff->TargetConfig().locale.c_str());
+
+            if (files.find(mf) != files.end()) {
+                fprintf(stderr, "%s: duplicate string resources for file %s\n",
+                        xliffFilename->c_str(), f->c_str());
+                fprintf(stderr, "%s: previously defined here.\n",
+                        files.find(mf)->xliffFilename.c_str());
+                err = 1;
+                continue;
+            }
+            files.insert(mf);
+        }
+    }
+
+    size_t deletedFileCount = 0;
+    size_t J = files.size() * 3;
+    size_t j = 1;
+    // Read all of the files from perforce.
+    for (MergedFileSet::iterator mf = files.begin(); mf != files.end(); mf++) {
+        MergedFile* file = const_cast<MergedFile*>(&(*mf));
+        // file->en_current
+        print_file_status(j++, J);
+        file->en_current = get_values_file(file->original, file->xliff->SourceConfig(),
+                                            CURRENT_VERSION, currentChange, true);
+        if (file->en_current == NULL) {
+            // deleted file
+            file->deleted_file = true;
+            deletedFileCount++;
+            continue;
+        }
+
+        // file->xx_current;
+        print_file_status(j++, J);
+        file->xx_current = get_values_file(file->translated, file->xliff->TargetConfig(),
+                                            CURRENT_VERSION, currentChange, false);
+        if (file->xx_current == NULL) {
+            file->xx_current = new ValuesFile(file->xliff->TargetConfig());
+            file->new_file = true;
+        }
+
+        // file->xx_old (note that the xliff's current version is our old version, because that
+        // was the current version when it was exported)
+        print_file_status(j++, J);
+        file->xx_old = get_values_file(file->translated, file->xliff->TargetConfig(),
+                                            OLD_VERSION, file->xliff->CurrentVersion(), false);
+        if (file->xx_old == NULL) {
+            file->xx_old = new ValuesFile(file->xliff->TargetConfig());
+            file->new_file = true;
+        }
+    }
+
+    // merge them
+    for (MergedFileSet::iterator mf = files.begin(); mf != files.end(); mf++) {
+        MergedFile* file = const_cast<MergedFile*>(&(*mf));
+        if (file->deleted_file) {
+            continue;
+        }
+        file->xx_new = merge_res_and_xliff(file->en_current, file->xx_current, file->xx_old,
+                                            file->original, file->xliff);
+    }
+
+    // now is a good time to stop if there was an error
+    if (err != 0) {
+        return err;
+    }
+
+    // locate the files
+    j = 1;
+    for (MergedFileSet::iterator mf = files.begin(); mf != files.end(); mf++) {
+        MergedFile* file = const_cast<MergedFile*>(&(*mf));
+        print_file_status(j++, J, "Locating");
+
+        file->xx_new_filename = Perforce::Where(file->translated, true);
+        if (file->xx_new_filename == "") {
+            fprintf(stderr, "\nWas not able to determine the location of depot file %s\n",
+                    file->translated.c_str());
+            err = 1;
+        }
+    }
+
+    if (err != 0) {
+        return err;
+    }
+
+    // p4 edit the files
+    // only do this if it changed - no need to submit files that haven't changed meaningfully
+    vector<string> filesToEdit;
+    vector<string> filesToAdd;
+    vector<string> filesToDelete;
+    for (MergedFileSet::iterator mf = files.begin(); mf != files.end(); mf++) {
+        MergedFile* file = const_cast<MergedFile*>(&(*mf));
+        if (file->deleted_file) {
+            filesToDelete.push_back(file->xx_new_filename);
+            continue;
+        }
+        string xx_current_text = file->xx_current->ToString();
+        string xx_new_text = file->xx_new->ToString();
+        if (xx_current_text != xx_new_text) {
+            if (file->xx_new->GetStrings().size() == 0) {
+                file->deleted_file = true;
+                filesToDelete.push_back(file->xx_new_filename);
+            } else {
+                file->xx_new_text = xx_new_text;
+                if (file->new_file) {
+                    filesToAdd.push_back(file->xx_new_filename);
+                } else {
+                    filesToEdit.push_back(file->xx_new_filename);
+                }
+            }
+        }
+    }
+    if (filesToAdd.size() == 0 && filesToEdit.size() == 0 && deletedFileCount == 0) {
+        printf("\nAll of the files are the same.  Nothing to change.\n");
+        return 0;
+    }
+    if (filesToEdit.size() > 0) {
+        printf("\np4 editing files...\n");
+        if (0 != Perforce::EditFiles(filesToEdit, true)) {
+            return 1;
+        }
+    }
+
+
+    printf("\n");
+
+    for (MergedFileSet::iterator mf = files.begin(); mf != files.end(); mf++) {
+        MergedFile* file = const_cast<MergedFile*>(&(*mf));
+        if (file->deleted_file) {
+            continue;
+        }
+        if (file->xx_new_text != "" && file->xx_new_filename != "") {
+            if (0 != write_to_file(file->xx_new_filename, file->xx_new_text)) {
+                err = 1;
+            }
+        }
+    }
+
+    if (err != 0) {
+        return err;
+    }
+
+    if (filesToAdd.size() > 0) {
+        printf("p4 adding %zd new files...\n", filesToAdd.size());
+        err = Perforce::AddFiles(filesToAdd, true);
+    }
+
+    if (filesToDelete.size() > 0) {
+        printf("p4 deleting %zd removed files...\n", filesToDelete.size());
+        err = Perforce::DeleteFiles(filesToDelete, true);
+    }
+
+    if (err != 0) {
+        return err;
+    }
+
+    printf("\n"
+           "Theoretically, this merge was successfull.  Next you should\n"
+           "review the diffs, get a code review, and submit it.  Enjoy.\n\n");
+    return 0;
+}
+
diff --git a/tools/localize/merge_res_and_xliff.h b/tools/localize/merge_res_and_xliff.h
new file mode 100644
index 0000000..acf2fff
--- /dev/null
+++ b/tools/localize/merge_res_and_xliff.h
@@ -0,0 +1,13 @@
+#ifndef MERGE_RES_AND_XLIFF_H
+#define MERGE_RES_AND_XLIFF_H
+
+#include "ValuesFile.h"
+#include "XLIFFFile.h"
+
+ValuesFile* merge_res_and_xliff(const ValuesFile* en_current,
+                                const ValuesFile* xx_current, const ValuesFile* xx_old,
+                                const string& filename, const XLIFFFile* xliff);
+
+int do_merge(const vector<string>& xliffFilenames);
+
+#endif // MERGE_RES_AND_XLIFF_H
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
new file mode 100644
index 0000000..5a2b0f4
--- /dev/null
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -0,0 +1,47 @@
+#include "merge_res_and_xliff.h"
+
+
+int
+merge_test()
+{
+    Configuration english;
+        english.locale = "en_US";
+    Configuration translated;
+        translated.locale = "zz_ZZ";
+
+    ValuesFile* en_current = ValuesFile::ParseFile("testdata/merge_en_current.xml", english,
+                                                    CURRENT_VERSION, "3");
+    if (en_current == NULL) {
+        fprintf(stderr, "merge_test: unable to read testdata/merge_en_current.xml\n");
+        return 1;
+    }
+
+    ValuesFile* xx_current = ValuesFile::ParseFile("testdata/merge_xx_current.xml", translated,
+                                                    CURRENT_VERSION, "3");
+    if (xx_current == NULL) {
+        fprintf(stderr, "merge_test: unable to read testdata/merge_xx_current.xml\n");
+        return 1;
+    }
+    ValuesFile* xx_old = ValuesFile::ParseFile("testdata/merge_xx_old.xml", translated,
+                                                    OLD_VERSION, "2");
+    if (xx_old == NULL) {
+        fprintf(stderr, "merge_test: unable to read testdata/merge_xx_old.xml\n");
+        return 1;
+    }
+
+    XLIFFFile* xliff = XLIFFFile::Parse("testdata/merge.xliff");
+
+    ValuesFile* result = merge_res_and_xliff(en_current, xx_current, xx_old,
+                                "//device/tools/localize/testdata/res/values/strings.xml", xliff);
+
+    if (result == NULL) {
+        fprintf(stderr, "merge_test: result is NULL\n");
+        return 1;
+    }
+
+    printf("======= RESULT =======\n%s===============\n", result->ToString().c_str());
+
+    return 0;
+}
+
+
diff --git a/tools/localize/res_check.cpp b/tools/localize/res_check.cpp
new file mode 100644
index 0000000..0fab98a
--- /dev/null
+++ b/tools/localize/res_check.cpp
@@ -0,0 +1,106 @@
+#include "res_check.h"
+#include "localize.h"
+#include "file_utils.h"
+#include "ValuesFile.h"
+
+#include <stdio.h>
+
+static int check_file(const ValuesFile* file);
+static int check_value(const SourcePos& pos, const XMLNode* value);
+static int scan_for_unguarded_format(const SourcePos& pos, const XMLNode* value, int depth = 0);
+
+int
+do_rescheck(const vector<string>& files)
+{
+    int err;
+
+    Configuration english;
+        english.locale = "en_US";
+
+    for (size_t i=0; i<files.size(); i++) {
+        const string filename = files[i];
+        ValuesFile* valuesFile = get_local_values_file(filename, english, CURRENT_VERSION,
+                "0", true);
+        if (valuesFile != NULL) {
+            err |= check_file(valuesFile);
+            delete valuesFile;
+        } else {
+            err |= 1;
+        }
+    }
+
+    return err;
+}
+
+static int
+check_file(const ValuesFile* file)
+{
+    int err = 0;
+    set<StringResource> strings = file->GetStrings();
+    for (set<StringResource>::iterator it=strings.begin(); it!=strings.end(); it++) {
+        XMLNode* value = it->value;
+        if (value != NULL) {
+            err |= check_value(it->pos, value);
+        }
+    }
+    return err;
+}
+
+static bool
+contains_percent(const string& str)
+{
+    const size_t len = str.length();
+    for (size_t i=0; i<len; i++) {
+        char c = str[i];
+        if (c == '%') {
+            return true;
+        }
+    }
+    return false;
+}
+
+static int
+check_value(const SourcePos& pos, const XMLNode* value)
+{
+    int err = 0;
+    err |= scan_for_unguarded_format(pos, value);
+    return err;
+}
+
+static bool
+is_xliff_block(const string& ns, const string& name)
+{
+    if (ns == XLIFF_XMLNS) {
+        return name == "g";
+    } else {
+        return false;
+    }
+}
+
+static int
+scan_for_unguarded_format(const SourcePos& pos, const string& string)
+{
+    bool containsPercent = contains_percent(string);
+    if (containsPercent) {
+        pos.Error("unguarded percent: '%s'\n", string.c_str());
+    }
+    return 0;
+}
+
+static int
+scan_for_unguarded_format(const SourcePos& pos, const XMLNode* value, int depth)
+{
+    if (value->Type() == XMLNode::ELEMENT) {
+        int err = 0;
+        if (depth == 0 || !is_xliff_block(value->Namespace(), value->Name())) {
+            const vector<XMLNode*>& children = value->Children();
+            for (size_t i=0; i<children.size(); i++) {
+                err |= scan_for_unguarded_format(pos, children[i], depth+1);
+            }
+        }
+        return err;
+    } else {
+        return scan_for_unguarded_format(pos, value->Text());
+    }
+}
+
diff --git a/tools/localize/res_check.h b/tools/localize/res_check.h
new file mode 100644
index 0000000..86e7ce6
--- /dev/null
+++ b/tools/localize/res_check.h
@@ -0,0 +1,12 @@
+#ifndef RESCHECK_H
+#define RESCHECK_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+int do_rescheck(const vector<string>& files);
+
+#endif // RESCHECK_H
diff --git a/tools/localize/test.cpp b/tools/localize/test.cpp
new file mode 100644
index 0000000..5fa2c17
--- /dev/null
+++ b/tools/localize/test.cpp
@@ -0,0 +1,31 @@
+#include "SourcePos.h"
+#include <stdio.h>
+
+int ValuesFile_test();
+int XLIFFFile_test();
+int XMLHandler_test();
+int Perforce_test();
+int localize_test();
+int merge_test();
+
+int
+test()
+{
+    bool all = true;
+    int err = 0;
+
+    if (all) err |= XMLHandler_test();
+    if (all) err |= ValuesFile_test();
+    if (all) err |= XLIFFFile_test();
+    if (all) err |= Perforce_test();
+    if (all) err |= localize_test();
+    if (all) err |= merge_test();
+
+    if (err != 0) {
+        fprintf(stderr, "some tests failed\n");
+    } else {
+        fprintf(stderr, "all tests passed\n");
+    }
+
+    return err;
+}
diff --git a/tools/localize/testdata/config.xml b/tools/localize/testdata/config.xml
new file mode 100644
index 0000000..affa140
--- /dev/null
+++ b/tools/localize/testdata/config.xml
@@ -0,0 +1,15 @@
+<localize-config>
+    <configuration id="system"
+            old-cl="1"
+            new-cl="43019">
+        <app dir="apps/common" />
+    </configuration>
+    <configuration id="samples"
+            old-cl="24801"
+            new-cl="43019">
+        <app dir="samples/NotePad" />
+        <reject file="samples/NotePad/res/values/strings.xml" name="string:menu_delete">
+            QA says this sounds <b>rude</b>.
+        </reject>
+    </configuration>
+</localize-config>
diff --git a/tools/localize/testdata/import.xliff b/tools/localize/testdata/import.xliff
new file mode 100644
index 0000000..b99b739
--- /dev/null
+++ b/tools/localize/testdata/import.xliff
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2"
+        version="1.2"
+        >
+    <file datatype="x-android-res"
+            original="//device/tools/localize/testdata/res/values/strings.xml"
+            product-version="1.0"
+            date="08:10:54 12/07/07 PST"
+            source-language="en_US"
+            product-name="kila"
+            target-language="zz_ZZ"
+            build-num="44391"
+            >
+        <body>
+            <trans-unit id="string:changed_in_xx">
+                <source>aaa</source>
+                <target>AAA</target>
+            </trans-unit>
+            <trans-unit id="string:first_translation">
+                <source>bbb</source>
+                <target>BBB</target>
+            </trans-unit>
+            <trans-unit id="string:deleted_string">
+                <source>ddd</source>
+                <target>DDDD</target>
+            </trans-unit>
+            <trans-unit id="array:0:growing_array">
+                <source>1-One</source>
+                <target>1-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:growing_array">
+                <source>1-Two</source>
+                <target>1-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:growing_array">
+                <source>1-Three</source>
+                <target>1-tHREE</target>
+            </trans-unit>
+            <trans-unit id="array:0:shrinking_array">
+                <source>2-One</source>
+                <target>2-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:shrinking_array">
+                <source>2-Two</source>
+                <target>2-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:shrinking_array">
+                <source>2-Three</source>
+                <target>2-tHREE</target>
+            </trans-unit>
+            <trans-unit id="array:3:shrinking_array">
+                <source>2-Four</source>
+                <target>2-fOUR</target>
+            </trans-unit>
+            <trans-unit id="array:0:deleted_array">
+                <source>4-One</source>
+                <target>4-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:deleted_array">
+                <source>4-Two</source>
+                <target>4-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:deleted_array">
+                <source>4-Three</source>
+                <target>4-tHREE</target>
+            </trans-unit>
+
+        </body>
+    </file>
+</xliff>
+
+
diff --git a/tools/localize/testdata/merge.xliff b/tools/localize/testdata/merge.xliff
new file mode 100644
index 0000000..2b78c45
--- /dev/null
+++ b/tools/localize/testdata/merge.xliff
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2"
+        version="1.2"
+        >
+    <file datatype="x-android-res"
+            original="testdata/merge_en_current.xml"
+            product-version="1.0"
+            date="08:10:54 12/07/07 PST"
+            source-language="en-US"
+            product-name="kila"
+            target-language="zz-ZZ"
+            build-num="44391"
+            >
+        <body>
+            <trans-unit id="string:changed_in_xx">
+                <source>aaa</source>
+                <target>AAA</target>
+            </trans-unit>
+            <trans-unit id="string:first_translation">
+                <source>bbb</source>
+                <target>BBB</target>
+            </trans-unit>
+            <trans-unit id="string:deleted_string">
+                <source>ddd</source>
+                <target>DDDD</target>
+            </trans-unit>
+            <trans-unit id="array:0:growing_array">
+                <source>1-One</source>
+                <target>1-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:growing_array">
+                <source>1-Two</source>
+                <target>1-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:growing_array">
+                <source>1-Three</source>
+                <target>1-tHREE</target>
+            </trans-unit>
+            <trans-unit id="array:0:shrinking_array">
+                <source>2-One</source>
+                <target>2-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:shrinking_array">
+                <source>2-Two</source>
+                <target>2-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:shrinking_array">
+                <source>2-Three</source>
+                <target>2-tHREE</target>
+            </trans-unit>
+            <trans-unit id="array:3:shrinking_array">
+                <source>2-Four</source>
+                <target>2-fOUR</target>
+            </trans-unit>
+            <trans-unit id="array:0:deleted_array">
+                <source>4-One</source>
+                <target>4-oNE</target>
+            </trans-unit>
+            <trans-unit id="array:1:deleted_array">
+                <source>4-Two</source>
+                <target>4-tWO</target>
+            </trans-unit>
+            <trans-unit id="array:2:deleted_array">
+                <source>4-Three</source>
+                <target>4-tHREE</target>
+            </trans-unit>
+
+        </body>
+    </file>
+</xliff>
+
+
diff --git a/tools/localize/testdata/merge_en_current.xml b/tools/localize/testdata/merge_en_current.xml
new file mode 100644
index 0000000..6a11e68
--- /dev/null
+++ b/tools/localize/testdata/merge_en_current.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">aaa</string>
+    <string name="first_translation">bbb</string>
+    <string name="previously_translated">ccc</string>
+    <string name="new_string">ccc</string>
+
+    <string name="formatted_string"><b>bold</b><i>italic<u>italic_underline</u></i><u>underline</u></string>
+
+    <array name="growing_array">
+        <!-- somebody wrote a comment! -->
+        <item>1-One</item>
+        <item>1-Two</item>
+        <item>1-Three</item>
+        <item>1-Four</item>
+    </array>
+    <array name="shrinking_array">
+        <!-- somebody wrote a comment! -->
+        <item>2-One</item>
+        <item>2-Two</item>
+        <item>2-Three</item>
+    </array>
+    <array name="new_array">
+        <!-- somebody wrote a comment! -->
+        <item>3-One</item>
+        <item>3-Two</item>
+        <item>3-Three</item>
+    </array>
+</resources>
diff --git a/tools/localize/testdata/merge_en_old.xml b/tools/localize/testdata/merge_en_old.xml
new file mode 100644
index 0000000..933f98e
--- /dev/null
+++ b/tools/localize/testdata/merge_en_old.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">aaa</string>
+    <string name="first_translation">bbb</string>
+    <string name="previously_translated">ccc</string>
+    <string name="deleted_string">ddd</string>
+
+    <string name="formatted_string"><b>bold</b><i>italic<u>italic_underline</u></i><u>underline</u></string>
+
+    <array name="growing_array">
+        <!-- somebody wrote a comment! -->
+        <item>1-One</item>
+        <item>1-Two</item>
+        <item>1-Three</item>
+    </array>
+    <array name="shrinking_array">
+        <!-- somebody wrote a comment! -->
+        <item>2-One</item>
+        <item>2-Two</item>
+        <item>2-Three</item>
+        <item>2-Four</item>
+    </array>
+    <array name="deleted_array">
+        <!-- somebody wrote a comment! -->
+        <item>4-One</item>
+        <item>4-Two</item>
+        <item>4-Three</item>
+    </array>
+</resources>
+
diff --git a/tools/localize/testdata/merge_xx_current.xml b/tools/localize/testdata/merge_xx_current.xml
new file mode 100644
index 0000000..c2a783d
--- /dev/null
+++ b/tools/localize/testdata/merge_xx_current.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">AAAA</string>
+    <string name="previously_translated">CCC</string>
+</resources>
+
+
diff --git a/tools/localize/testdata/merge_xx_old.xml b/tools/localize/testdata/merge_xx_old.xml
new file mode 100644
index 0000000..9d3a7d8
--- /dev/null
+++ b/tools/localize/testdata/merge_xx_old.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">aaa</string>
+    <string name="previously_translated">CCC</string>
+</resources>
+
diff --git a/tools/localize/testdata/pseudo.xliff b/tools/localize/testdata/pseudo.xliff
new file mode 100644
index 0000000..5b44f86
--- /dev/null
+++ b/tools/localize/testdata/pseudo.xliff
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2"
+        version="1.2"
+        >
+    <file datatype="x-android-res"
+            original="//device/tools/localization/tests/res/values/strings.xml"
+            product-version="1.0"
+            date="08:10:54 12/07/07 PST"
+            source-language="en-US"
+            product-name="kila"
+            target-language="zz-ZZ"
+            build-num="32138"
+            >
+        <body>
+            <trans-unit id="string:complex">
+                <source>First <g id="string:complex:0" ctype="underline">underline</g>, <g id="string:complex:1" ctype="italic">italic<g id="string:complex:2" ctype="bold">italicbold</g></g> End </source>
+            </trans-unit>
+            <trans-unit id="string:complex-quoted">
+                <source xml:space="preserve">First <g id="string:complex-quoted:0" ctype="underline">underline</g>, <g id="string:complex-quoted:1" ctype="italic">italic<g id="string:complex-quoted:2" ctype="bold">italicbold</g></g> End</source>
+            </trans-unit>
+            <trans-unit id="string:simple">
+                <source>Simple</source>
+            </trans-unit>
+            <trans-unit id="array:0:simple">
+                <source>Simple</source>
+            </trans-unit>
+            <trans-unit id="array:1:simple">
+                <source>Simple</source>
+            </trans-unit>
+            <trans-unit id="string:simple-quoted">
+                <source xml:space="preserve"> Quote</source>
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve"> OLD Quote</source>
+                    <target xml:lang="xx"> OLD Ờũỡŧę</target>
+                </alt-trans>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
+
diff --git a/tools/localize/testdata/res/values-zz-rZZ/strings.xml b/tools/localize/testdata/res/values-zz-rZZ/strings.xml
new file mode 100644
index 0000000..c2a783d
--- /dev/null
+++ b/tools/localize/testdata/res/values-zz-rZZ/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">AAAA</string>
+    <string name="previously_translated">CCC</string>
+</resources>
+
+
diff --git a/tools/localize/testdata/res/values/strings.xml b/tools/localize/testdata/res/values/strings.xml
new file mode 100644
index 0000000..6a11e68
--- /dev/null
+++ b/tools/localize/testdata/res/values/strings.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="changed_in_xx">aaa</string>
+    <string name="first_translation">bbb</string>
+    <string name="previously_translated">ccc</string>
+    <string name="new_string">ccc</string>
+
+    <string name="formatted_string"><b>bold</b><i>italic<u>italic_underline</u></i><u>underline</u></string>
+
+    <array name="growing_array">
+        <!-- somebody wrote a comment! -->
+        <item>1-One</item>
+        <item>1-Two</item>
+        <item>1-Three</item>
+        <item>1-Four</item>
+    </array>
+    <array name="shrinking_array">
+        <!-- somebody wrote a comment! -->
+        <item>2-One</item>
+        <item>2-Two</item>
+        <item>2-Three</item>
+    </array>
+    <array name="new_array">
+        <!-- somebody wrote a comment! -->
+        <item>3-One</item>
+        <item>3-Two</item>
+        <item>3-Three</item>
+    </array>
+</resources>
diff --git a/tools/localize/testdata/strip_xliff.xliff b/tools/localize/testdata/strip_xliff.xliff
new file mode 100644
index 0000000..9254cf2
--- /dev/null
+++ b/tools/localize/testdata/strip_xliff.xliff
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2"
+        version="1.2"
+        >
+    <file datatype="x-android-res"
+            original="//device/tools/localization/tests/res/values/strings.xml"
+            product-version="1.0"
+            date="08:10:54 12/07/07 PST"
+            source-language="en-US"
+            product-name="kila"
+            target-language="zz-ZZ"
+            build-num="32138"
+            >
+        <body>
+
+            <trans-unit id="string:string-000-0">
+            </trans-unit>
+            <trans-unit id="string:string-001-0">
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve">source</source>
+                </alt-trans>
+            </trans-unit>
+            <trans-unit id="string:string-010-0">
+                <alt-trans>
+                    <target xml:lang="zz" xml:space="preserve">target</target>
+                </alt-trans>
+            </trans-unit>
+            <trans-unit id="string:string-011-0">
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve">source</source>
+                    <target xml:lang="zz" xml:space="preserve">target</target>
+                </alt-trans>
+            </trans-unit>
+
+            <trans-unit id="string:string-100-1">
+                <source xml:space="preserve">source</source>
+            </trans-unit>
+            <trans-unit id="string:string-101-1">
+                <source xml:space="preserve">source</source>
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve">source</source>
+                </alt-trans>
+            </trans-unit>
+            <trans-unit id="string:string-110-1">
+                <source xml:space="preserve">source</source>
+                <alt-trans>
+                    <target xml:lang="zz" xml:space="preserve">target</target>
+                </alt-trans>
+            </trans-unit>
+
+            <trans-unit id="string:string-111-0">
+                <source xml:space="preserve">source</source>
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve">source</source>
+                    <target xml:lang="zz" xml:space="preserve">target</target>
+                </alt-trans>
+            </trans-unit>
+            <trans-unit id="string:string-111-1">
+                <source xml:space="preserve">source</source>
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve">alt-source</source>
+                    <target xml:lang="zz" xml:space="preserve">target</target>
+                </alt-trans>
+            </trans-unit>
+
+        </body>
+    </file>
+</xliff>
+
+
diff --git a/tools/localize/testdata/values/strings.xml b/tools/localize/testdata/values/strings.xml
new file mode 100644
index 0000000..5e8d43d
--- /dev/null
+++ b/tools/localize/testdata/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="test1">Discard</string>
+    <!-- comment -->
+    <string name="test2">a<b>b<i>c</i></b>d</string>
+    <string name="test3">a<xliff:g  a="b" xliff:a="asdf">bBb</xliff:g>C</string>
+
+    <!-- Email address types from android.provider.Contacts -->
+    <array name="emailAddressTypes">
+        <!-- somebody wrote a comment! -->
+        <item>Email</item>
+        <item>Home</item>
+        <item>Work</item>
+        <item>Other\u2026</item>
+    </array>
+</resources>
diff --git a/tools/localize/testdata/xliff1.xliff b/tools/localize/testdata/xliff1.xliff
new file mode 100644
index 0000000..55a8d8e
--- /dev/null
+++ b/tools/localize/testdata/xliff1.xliff
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2"
+        version="1.2"
+        >
+    <file datatype="x-android-res"
+            original="//device/tools/localization/tests/res/values/strings.xml"
+            product-version="1.0"
+            date="08:10:54 12/07/07 PST"
+            source-language="en-US"
+            product-name="kila"
+            target-language="zz-ZZ"
+            build-num="32138"
+            >
+        <body>
+            <trans-unit id="string:complex">
+                <source>First <g id="string:complex:0" ctype="underline">underline</g>, <g id="string:complex:1" ctype="italic">italic<g id="string:complex:2" ctype="bold">italicbold</g></g> End </source>
+                <target>Ḟịṙṩŧ ,  Ḛŋḋ </target>
+            </trans-unit>
+            <trans-unit id="string:complex-quoted">
+                <source xml:space="preserve">First <g id="string:complex-quoted:0" ctype="underline">underline</g>, <g id="string:complex-quoted:1" ctype="italic">italic<g id="string:complex-quoted:2" ctype="bold">italicbold</g></g> End</source>
+                <target>Ḟịṙṩŧ ,  Ḛŋḋ</target>
+            </trans-unit>
+            <trans-unit id="string:simple">
+                <source>Simple</source>
+                <target>Ṩịṃṕļę</target>
+            </trans-unit>
+            <trans-unit id="array:0:simple">
+                <source>Simple</source>
+                <target>Ṩịṃṕļę</target>
+            </trans-unit>
+            <trans-unit id="array:1:simple">
+                <source>Simple</source>
+                <target>Ṩịṃṕļę</target>
+            </trans-unit>
+            <trans-unit id="string:simple-quoted">
+                <source xml:space="preserve"> Quote</source>
+                <target> Ờũỡŧę</target>
+                <alt-trans>
+                    <source xml:lang="en" xml:space="preserve"> OLD Quote</source>
+                    <target xml:lang="xx"> OLD Ờũỡŧę</target>
+                </alt-trans>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
+
diff --git a/tools/localize/testdata/xml.xml b/tools/localize/testdata/xml.xml
new file mode 100644
index 0000000..ef930d0
--- /dev/null
+++ b/tools/localize/testdata/xml.xml
@@ -0,0 +1,16 @@
+<ASDF>
+    <a id="system"
+            old-cl="1"
+            new-cl="43019">
+        <app dir="apps/common" />
+    </a>
+    <a id="samples"
+            old-cl="1"
+            new-cl="43019">asdf
+        <app dir="samples/NotePad" />
+        <app dir="samples/LunarLander" />
+        <something>a<b>,</b>b </something>
+        <exact xml:space="preserve">a<b>,</b>b </exact>
+    </a>
+</ASDF>
+
diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp
new file mode 100644
index 0000000..236705f
--- /dev/null
+++ b/tools/localize/xmb.cpp
@@ -0,0 +1,181 @@
+#include "xmb.h"
+
+#include "file_utils.h"
+#include "localize.h"
+#include "ValuesFile.h"
+#include "XMLHandler.h"
+#include "XLIFFFile.h"
+
+#include <map>
+
+using namespace std;
+
+const char *const NS_MAP[] = {
+    "xml", XMLNS_XMLNS,
+    NULL, NULL
+};
+
+set<string> g_tags;
+
+static string
+strip_newlines(const string& str)
+{
+    string res;
+    const size_t N = str.length();
+    for (size_t i=0; i<N; i++) {
+        char c = str[i];
+        if (c != '\n' && c != '\r') {
+            res += c;
+        } else {
+            res += ' ';
+        }
+    }
+    return res;
+}
+
+static int
+rename_id_attribute(XMLNode* node)
+{
+    vector<XMLAttribute>& attrs = node->EditAttributes();
+    const size_t I = attrs.size();
+    for (size_t i=0; i<I; i++) {
+        XMLAttribute attr = attrs[i];
+        if (attr.name == "id") {
+            attr.name = "name";
+            attrs.erase(attrs.begin()+i);
+            attrs.push_back(attr);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static int
+convert_xliff_to_ph(XMLNode* node, int* phID)
+{
+    int err = 0;
+    if (node->Type() == XMLNode::ELEMENT) {
+        if (node->Namespace() == XLIFF_XMLNS) {
+            g_tags.insert(node->Name());
+            node->SetName("", "ph");
+
+            err = rename_id_attribute(node);
+            if (err != 0) {
+                char name[30];
+                (*phID)++;
+                sprintf(name, "id-%d", *phID);
+                node->EditAttributes().push_back(XMLAttribute("", "name", name));
+                err = 0;
+            }
+        }
+        vector<XMLNode*>& children = node->EditChildren();
+        const size_t I = children.size();
+        for (size_t i=0; i<I; i++) {
+            err |= convert_xliff_to_ph(children[i], phID);
+        }
+    }
+    return err;
+}
+
+XMLNode*
+resource_to_xmb_msg(const StringResource& res)
+{
+    // the msg element
+    vector<XMLAttribute> attrs;
+    string name = res.pos.file;
+    name += ":";
+    name += res.TypedID();
+    attrs.push_back(XMLAttribute("", "name", name));
+    attrs.push_back(XMLAttribute("", "desc", strip_newlines(res.comment)));
+    attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve"));
+    XMLNode* msg = XMLNode::NewElement(res.pos, "", "msg", attrs, XMLNode::EXACT);
+
+    // the contents are in xliff/html, convert it to xliff
+    int err = 0;
+    XMLNode* value = res.value;
+    string tag = value->Name();
+    int phID = 0;
+    for (vector<XMLNode*>::const_iterator it=value->Children().begin();
+            it!=value->Children().end(); it++) {
+        err |= convert_html_to_xliff(*it, tag, msg, &phID);
+    }
+
+    if (err != 0) {
+        return NULL;
+    }
+
+    // and then convert that to xmb
+    for (vector<XMLNode*>::iterator it=msg->EditChildren().begin();
+            it!=msg->EditChildren().end(); it++) {
+        err |= convert_xliff_to_ph(*it, &phID);
+    }
+
+    if (err == 0) {
+        return msg;
+    } else {
+        return NULL;
+    }
+}
+
+int
+do_xlb_export(const string& outfile, const vector<string>& resFiles)
+{
+    int err = 0;
+
+    size_t totalFileCount = resFiles.size();
+
+    Configuration english;
+        english.locale = "en_US";
+
+    set<StringResource> allResources;
+
+    const size_t J = resFiles.size();
+    for (size_t j=0; j<J; j++) {
+        string resFile = resFiles[j];
+
+        ValuesFile* valuesFile = get_local_values_file(resFile, english, CURRENT_VERSION, "", true);
+        if (valuesFile != NULL) {
+            set<StringResource> resources = valuesFile->GetStrings();
+            allResources.insert(resources.begin(), resources.end());
+        } else {
+            fprintf(stderr, "error reading file %s\n", resFile.c_str());
+        }
+
+        delete valuesFile;
+    }
+
+    // Construct the XLB xml
+    vector<XMLAttribute> attrs;
+    attrs.push_back(XMLAttribute("", "locale", "en"));
+    XMLNode* localizationbundle = XMLNode::NewElement(GENERATED_POS, "", "localizationbundle",
+            attrs, XMLNode::PRETTY);
+
+    for (set<StringResource>::iterator it=allResources.begin(); it!=allResources.end(); it++) {
+        XMLNode* msg = resource_to_xmb_msg(*it);
+        if (msg) {
+            localizationbundle->EditChildren().push_back(msg);
+        } else {
+            err = 1;
+        }
+    }
+
+#if 0
+    for (set<string>::iterator it=g_tags.begin(); it!=g_tags.end(); it++) {
+        printf("tag: %s\n", it->c_str());
+    }
+    printf("err=%d\n", err);
+#endif
+    if (err == 0) {
+        FILE* f = fopen(outfile.c_str(), "wb");
+        if (f == NULL) {
+            fprintf(stderr, "can't open outputfile: %s\n", outfile.c_str());
+            return 1;
+        }
+        fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+        fprintf(f, "%s\n", localizationbundle->ToString(NS_MAP).c_str());
+        fclose(f);
+    }
+
+    return err;
+}
+
diff --git a/tools/localize/xmb.h b/tools/localize/xmb.h
new file mode 100644
index 0000000..96492b1
--- /dev/null
+++ b/tools/localize/xmb.h
@@ -0,0 +1,11 @@
+#ifndef XMB_H
+#define XMB_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+int do_xlb_export(const string& outFile, const vector<string>& resFiles);
+
+#endif // XMB_H
diff --git a/tools/makekeycodes/Android.mk b/tools/makekeycodes/Android.mk
new file mode 100644
index 0000000..401d44e
--- /dev/null
+++ b/tools/makekeycodes/Android.mk
@@ -0,0 +1,13 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	makekeycodes.cpp
+
+LOCAL_MODULE := makekeycodes
+
+include $(BUILD_HOST_EXECUTABLE)
+
+
diff --git a/tools/makekeycodes/makekeycodes.cpp b/tools/makekeycodes/makekeycodes.cpp
new file mode 100644
index 0000000..16df774
--- /dev/null
+++ b/tools/makekeycodes/makekeycodes.cpp
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <ui/KeycodeLabels.h>
+
+int
+main(int argc, char** argv)
+{
+    // TODO: Add full copyright.
+    printf("// Copyright (C) 2008 The Android Open Source Project\n");
+    printf("//\n");
+    printf("// This file is generated by makekeycodes from the definitions.\n");
+    printf("// in includes/ui/KeycodeLabels.h.\n");
+    printf("//\n");
+    printf("// If you modify this, your changes will be overwritten.\n");
+    printf("\n");
+    printf("pacakge android.os;\n");
+    printf("\n");
+    printf("public class KeyEvent\n");
+    printf("{\n");
+
+    for (int i=0; KEYCODES[i].literal != NULL; i++) {
+        printf("    public static final int KEYCODE_%s = 0x%08x;\n",
+                KEYCODES[i].literal, KEYCODES[i].value);
+    }
+
+    printf("\n");
+    for (int i=0; FLAGS[i].literal != NULL; i++) {
+        printf("    public static final int MODIFIER_%s = 0x%08x;\n",
+                FLAGS[i].literal, FLAGS[i].value);
+    }
+
+    printf("}\n");
+    return 0;
+}
diff --git a/tools/preload/20080522.compiled b/tools/preload/20080522.compiled
new file mode 100644
index 0000000..a2af422
--- /dev/null
+++ b/tools/preload/20080522.compiled
Binary files differ
diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk
new file mode 100644
index 0000000..e6fa103
--- /dev/null
+++ b/tools/preload/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	ClassRank.java \
+	Compile.java  \
+	LoadedClass.java \
+	MemoryUsage.java \
+	Operation.java \
+	Policy.java \
+	PrintCsv.java \
+	PrintPsTree.java \
+	Proc.java \
+	Record.java \
+	Root.java \
+	WritePreloadedClassFile.java
+
+LOCAL_MODULE:= preload
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-subdir-makefiles)
diff --git a/tools/preload/ClassRank.java b/tools/preload/ClassRank.java
new file mode 100644
index 0000000..3699b89
--- /dev/null
+++ b/tools/preload/ClassRank.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Comparator;
+
+/**
+ * Ranks classes for preloading based on how long their operations took
+ * and how early the operations happened. Higher ranked classes come first.
+ */
+class ClassRank implements Comparator<Operation> {
+
+    /**
+     * Increase this number to add more weight to classes which were loaded
+     * earlier.
+     */
+    static final int SEQUENCE_WEIGHT = 500; // 5 ms
+
+    static final int BUCKET_SIZE = 5;
+
+    public int compare(Operation a, Operation b) {
+        // Higher ranked operations should come first.
+        int result = rankOf(b) - rankOf(a);
+        if (result != 0) {
+            return result;
+        }
+
+        // Make sure we don't drop one of two classes w/ the same rank.
+        // If a load and an initialization have the same rank, it's OK
+        // to treat the operations equally.
+        return a.loadedClass.name.compareTo(b.loadedClass.name);
+    }
+
+    /** Ranks the given operation. */
+    private static int rankOf(Operation o) {
+        return o.medianExclusiveTimeMicros()
+                + SEQUENCE_WEIGHT / (o.index / BUCKET_SIZE + 1);
+    }
+}
+
+
diff --git a/tools/preload/Compile.java b/tools/preload/Compile.java
new file mode 100644
index 0000000..67258ef
--- /dev/null
+++ b/tools/preload/Compile.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parses and analyzes a log, pulling our PRELOAD information. If you have
+ * an emulator or device running in the background, this class will use it
+ * to measure and record the memory usage of each class.
+ * 
+ * TODO: Should analyze lines and select substring dynamically (instead of hardcoded 19)
+ */
+public class Compile {
+
+    public static void main(String[] args) throws IOException {
+        if (args.length != 2) {
+            System.err.println("Usage: Compile [log file] [output file]");
+            System.exit(0);
+        }
+
+        Root root = new Root();
+
+        List<Record> records = new ArrayList<Record>();
+
+        BufferedReader in = new BufferedReader(new InputStreamReader(
+                new FileInputStream(args[0])));
+
+        String line;
+        int lineNumber = 0;
+        while ((line = in.readLine()) != null) {
+            lineNumber++;
+            if (line.startsWith("I/PRELOAD")) {
+                try {
+                    String clipped = line.substring(19);
+                    records.add(new Record(clipped, lineNumber));
+                } catch (RuntimeException e) {
+                    throw new RuntimeException(
+                            "Exception while recording line " + lineNumber + ": " + line, e);
+                }
+            }
+        }
+
+        for (Record record : records) {
+            root.indexProcess(record);
+        }
+
+        for (Record record : records) {
+            root.indexClassOperation(record);
+        }
+
+        in.close();
+
+        root.toFile(args[1]);
+    }
+}
diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java
new file mode 100644
index 0000000..5782807
--- /dev/null
+++ b/tools/preload/LoadedClass.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A loaded class.
+ */
+class LoadedClass implements Serializable, Comparable<LoadedClass> {
+
+    private static final long serialVersionUID = 0;
+
+    /** Class name. */
+    final String name;
+
+    /** Load operations. */
+    final List<Operation> loads = new ArrayList<Operation>();
+
+    /** Static initialization operations. */
+    final List<Operation> initializations = new ArrayList<Operation>();
+
+    /** Memory usage gathered by loading only this class in its own VM. */
+    MemoryUsage memoryUsage = MemoryUsage.NOT_AVAILABLE;
+
+    /**
+     * Whether or not this class was loaded in the system class loader.
+     */
+    final boolean systemClass;
+
+    /** Whether or not this class will be preloaded. */
+    boolean preloaded;
+
+    /** Constructs a new class. */
+    LoadedClass(String name, boolean systemClass) {
+        this.name = name;
+        this.systemClass = systemClass;
+    }
+
+    void measureMemoryUsage() {
+        this.memoryUsage = MemoryUsage.forClass(name);        
+    }
+
+    int mlt = -1;
+
+    /** Median time to load this class. */
+    int medianLoadTimeMicros() {
+        if (mlt != -1) {
+            return mlt;
+        }
+
+        return mlt = calculateMedian(loads);
+    }
+
+    int mit = -1;
+
+    /** Median time to initialize this class. */
+    int medianInitTimeMicros() {
+        if (mit != -1) {
+            return mit;
+        }
+
+        return mit = calculateMedian(initializations);
+    }
+
+    /** Calculates the median duration for a list of operations. */
+    private static int calculateMedian(List<Operation> operations) {
+        int size = operations.size();
+        if (size == 0) {
+            return 0;
+        }
+
+        int[] times = new int[size];
+        for (int i = 0; i < size; i++) {
+            times[i] = operations.get(i).exclusiveTimeMicros();
+        }
+
+        Arrays.sort(times);
+        int middle = size / 2;
+        if (size % 2 == 1) {
+            // Odd
+            return times[middle];
+        } else {
+            // Even -- average the two.
+            return (times[middle - 1] + times[middle]) / 2;
+        }
+    }
+
+    /**
+     * Counts loads by apps.
+     */
+    int appLoads() {
+        return operationsByApps(loads);
+    }
+
+    /**
+     * Counts inits by apps.
+     */
+    int appInits() {
+        return operationsByApps(initializations);
+    }
+
+    /**
+     * Counts number of app operations in the given list.
+     */
+    private static int operationsByApps(List<Operation> operations) {
+        int byApps = 0;
+        for (Operation operation : operations) {
+            if (operation.process.isApplication()) {
+                byApps++;
+            }
+        }
+        return byApps;
+    }
+
+    public int compareTo(LoadedClass o) {
+        return name.compareTo(o.name);
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Returns true if this class's initialization causes the given class to
+     * initialize.
+     */
+    public boolean initializes(LoadedClass clazz, Set<LoadedClass> visited) {
+        // Avoid infinite recursion.
+        if (!visited.add(this)) {
+            return false;
+        }
+
+        if (clazz == this) {
+            return true;
+        }
+
+        for (Operation initialization : initializations) {
+            if (initialization.loadedClass.initializes(clazz, visited)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java
new file mode 100644
index 0000000..e5dfb2a
--- /dev/null
+++ b/tools/preload/MemoryUsage.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Memory usage information.
+ */
+class MemoryUsage implements Serializable {
+
+    private static final long serialVersionUID = 0;
+
+    static final MemoryUsage NOT_AVAILABLE = new MemoryUsage();
+    
+    static int errorCount = 0;
+    static final int MAXIMUM_ERRORS = 10;        // give up after this many fails
+
+    final int nativeSharedPages;
+    final int javaSharedPages;
+    final int otherSharedPages;
+    final int nativePrivatePages;
+    final int javaPrivatePages;
+    final int otherPrivatePages;
+
+    final int allocCount;
+    final int allocSize;
+    final int freedCount;
+    final int freedSize;
+    final long nativeHeapSize;
+
+    public MemoryUsage(String line) {
+        String[] parsed = line.split(",");
+
+        nativeSharedPages = Integer.parseInt(parsed[1]);
+        javaSharedPages = Integer.parseInt(parsed[2]);
+        otherSharedPages = Integer.parseInt(parsed[3]);
+        nativePrivatePages = Integer.parseInt(parsed[4]);
+        javaPrivatePages = Integer.parseInt(parsed[5]);
+        otherPrivatePages = Integer.parseInt(parsed[6]);
+        allocCount = Integer.parseInt(parsed[7]);
+        allocSize = Integer.parseInt(parsed[8]);
+        freedCount = Integer.parseInt(parsed[9]);
+        freedSize = Integer.parseInt(parsed[10]);
+        nativeHeapSize = Long.parseLong(parsed[11]);
+    }
+
+    MemoryUsage() {
+        nativeSharedPages = -1;
+        javaSharedPages = -1;
+        otherSharedPages = -1;
+        nativePrivatePages = -1;
+        javaPrivatePages = -1;
+        otherPrivatePages = -1;
+
+        allocCount = -1;
+        allocSize = -1;
+        freedCount = -1;
+        freedSize = -1;
+        nativeHeapSize = -1;
+    }
+
+    MemoryUsage(int nativeSharedPages,
+            int javaSharedPages,
+            int otherSharedPages,
+            int nativePrivatePages,
+            int javaPrivatePages,
+            int otherPrivatePages,
+            int allocCount,
+            int allocSize,
+            int freedCount,
+            int freedSize,
+            long nativeHeapSize) {
+        this.nativeSharedPages = nativeSharedPages;
+        this.javaSharedPages = javaSharedPages;
+        this.otherSharedPages = otherSharedPages;
+        this.nativePrivatePages = nativePrivatePages;
+        this.javaPrivatePages = javaPrivatePages;
+        this.otherPrivatePages = otherPrivatePages;
+        this.allocCount = allocCount;
+        this.allocSize = allocSize;
+        this.freedCount = freedCount;
+        this.freedSize = freedSize;
+        this.nativeHeapSize = nativeHeapSize;
+    }
+
+    MemoryUsage subtract(MemoryUsage baseline) {
+        return new MemoryUsage(
+                nativeSharedPages - baseline.nativeSharedPages,
+                javaSharedPages - baseline.javaSharedPages,
+                otherSharedPages - baseline.otherSharedPages,
+                nativePrivatePages - baseline.nativePrivatePages,
+                javaPrivatePages - baseline.javaPrivatePages,
+                otherPrivatePages - baseline.otherPrivatePages,
+                allocCount - baseline.allocCount,
+                allocSize - baseline.allocSize,
+                freedCount - baseline.freedCount,
+                freedSize - baseline.freedSize,
+                nativeHeapSize - baseline.nativeHeapSize);
+    }
+
+    int javaHeapSize() {
+        return allocSize - freedSize;
+    }
+
+    int javaPagesInK() {
+        return (javaSharedPages + javaPrivatePages) * 4;
+    }
+
+    int nativePagesInK() {
+        return (nativeSharedPages + nativePrivatePages) * 4;
+    }
+    int otherPagesInK() {
+        return (otherSharedPages + otherPrivatePages) * 4;
+    }
+
+    /**
+     * Was this information available?
+     */
+    boolean isAvailable() {
+        return nativeSharedPages != -1;
+    }
+
+    /**
+     * Measures baseline memory usage.
+     */
+    static MemoryUsage baseline() {
+        return forClass(null);
+    }
+
+    private static final String CLASS_PATH = "-Xbootclasspath"
+            + ":/system/framework/core.jar"
+            + ":/system/framework/ext.jar"
+            + ":/system/framework/framework.jar"
+            + ":/system/framework/framework-tests.jar"
+            + ":/system/framework/services.jar"
+            + ":/system/framework/loadclass.jar";
+
+    private static final String[] GET_DIRTY_PAGES = {
+        "adb", "-e", "shell", "dalvikvm", CLASS_PATH, "LoadClass" };
+
+    /**
+     * Measures memory usage for the given class.
+     */
+    static MemoryUsage forClass(String className) {
+        
+        // This is a coarse approximation for determining that no device is connected,
+        // or that the communication protocol has changed, but we'll keep going and stop whining.
+        if (errorCount >= MAXIMUM_ERRORS) {
+            return NOT_AVAILABLE;
+        }
+        
+        MeasureWithTimeout measurer = new MeasureWithTimeout(className);
+
+        new Thread(measurer).start();
+
+        synchronized (measurer) {
+            if (measurer.memoryUsage == null) {
+                // Wait up to 10s.
+                try {
+                    measurer.wait(30000);
+                } catch (InterruptedException e) {
+                    System.err.println("Interrupted waiting for measurement.");
+                    e.printStackTrace();
+                    return NOT_AVAILABLE;
+                }
+
+                // If it's still null.
+                if (measurer.memoryUsage == null) {
+                    System.err.println("Timed out while measuring "
+                            + className + ".");
+                    return NOT_AVAILABLE;
+                }
+            }
+
+            System.err.println("Got memory usage for " + className + ".");
+            return measurer.memoryUsage;
+        }
+    }
+
+    static class MeasureWithTimeout implements Runnable {
+
+        final String className;
+        MemoryUsage memoryUsage = null;
+
+        MeasureWithTimeout(String className) {
+            this.className = className;
+        }
+
+        public void run() {
+            MemoryUsage measured = measure();
+
+            synchronized (this) {
+                memoryUsage = measured;
+                notifyAll();
+            }
+        }
+
+        private MemoryUsage measure() {
+            String[] commands = GET_DIRTY_PAGES;
+            if (className != null) {
+                List<String> commandList = new ArrayList<String>(
+                        GET_DIRTY_PAGES.length + 1);
+                commandList.addAll(Arrays.asList(commands));
+                commandList.add(className);
+                commands = commandList.toArray(new String[commandList.size()]);
+            }
+
+            try {
+                final Process process = Runtime.getRuntime().exec(commands);
+
+                final InputStream err = process.getErrorStream();
+
+                // Send error output to stderr.
+                Thread errThread = new Thread() {
+                    @Override
+                    public void run() {
+                        copy(err, System.err);
+                    }
+                };
+                errThread.setDaemon(true);
+                errThread.start();
+
+                BufferedReader in = new BufferedReader(
+                        new InputStreamReader(process.getInputStream()));
+                String line = in.readLine();
+                if (line == null || !line.startsWith("DECAFBAD,")) {
+                    System.err.println("Got bad response for " + className
+                            + ": " + line);
+                    errorCount += 1;
+                    return NOT_AVAILABLE;
+                }
+
+                in.close();
+                err.close();
+                process.destroy();                
+
+                return new MemoryUsage(line);
+            } catch (IOException e) {
+                System.err.println("Error getting stats for "
+                        + className + ".");                
+                e.printStackTrace();
+                return NOT_AVAILABLE;
+            }
+        }
+
+    }
+
+    /**
+     * Copies from one stream to another.
+     */
+    private static void copy(InputStream in, OutputStream out) {
+        byte[] buffer = new byte[1024];
+        int read;
+        try {
+            while ((read = in.read(buffer)) > -1) {
+                out.write(buffer, 0, read);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/tools/preload/Operation.java b/tools/preload/Operation.java
new file mode 100644
index 0000000..4f1938e
--- /dev/null
+++ b/tools/preload/Operation.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.Serializable;
+
+/**
+ * An operation with a duration. Could represent a class load or initialization.
+ */
+class Operation implements Serializable {
+
+    private static final long serialVersionUID = 0;
+    
+    /**
+     * Type of operation.
+     */
+    enum Type {
+        LOAD, INIT
+    }
+
+    /** Process this operation occurred in. */
+    final Proc process;
+
+    /** Start time for this operation. */
+    final long startTimeNanos;
+
+    /** Index of this operation relative to its process. */
+    final int index;
+
+    /** Type of operation. */
+    final Type type;
+
+    /** End time for this operation. */
+    long endTimeNanos = -1;
+
+    /** The class that this operation loaded or initialized. */
+    final LoadedClass loadedClass;
+
+    /** Other operations that occurred during this one. */
+    final List<Operation> subops = new ArrayList<Operation>();
+
+    /** Constructs a new operation. */
+    Operation(Proc process, LoadedClass loadedClass, long startTimeNanos,
+            int index, Type type) {
+        this.process = process;
+        this.loadedClass = loadedClass;
+        this.startTimeNanos = startTimeNanos;
+        this.index = index;
+        this.type = type;
+    }
+
+    /**
+     * Returns how long this class initialization and all the nested class
+     * initializations took.
+     */
+    private long inclusiveTimeNanos() {
+        if (endTimeNanos == -1) {
+            throw new IllegalStateException("End time hasn't been set yet: "
+                    + loadedClass.name);
+        }
+
+        return endTimeNanos - startTimeNanos;
+    }
+
+    /**
+     * Returns how long this class initialization took.
+     */
+    int exclusiveTimeMicros() {
+        long exclusive = inclusiveTimeNanos();
+
+        for (Operation child : subops) {
+            exclusive -= child.inclusiveTimeNanos();
+        }
+
+        if (exclusive < 0) {
+            throw new AssertionError(loadedClass.name);
+        }
+
+        return nanosToMicros(exclusive);
+    }
+
+    /** Gets the median time that this operation took across all processes. */
+    int medianExclusiveTimeMicros() {
+        switch (type) {
+            case LOAD: return loadedClass.medianLoadTimeMicros();
+            case INIT: return loadedClass.medianInitTimeMicros();
+            default: throw new AssertionError();
+        }
+    }
+
+    /**
+     * Converts nanoseconds to microseconds.
+     *
+     * @throws RuntimeException if overflow occurs
+     */
+    private static int nanosToMicros(long nanos) {
+        long micros = nanos / 1000;
+        int microsInt = (int) micros;
+        if (microsInt != micros) {
+            throw new RuntimeException("Integer overflow: " + nanos);
+        }
+        return microsInt;
+    }
+    
+    /**
+     * Primarily for debugger support
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(type.toString());
+        sb.append(' ');
+        sb.append(loadedClass.toString());
+        if (subops.size() > 0) {
+            sb.append(" (");
+            sb.append(subops.size());
+            sb.append(" sub ops)");
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java
new file mode 100644
index 0000000..554966b
--- /dev/null
+++ b/tools/preload/Policy.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This is not instantiated - we just provide data for other classes to use
+ */
+public class Policy {
+    
+    /**
+     * This location (in the build system) of the preloaded-classes file.
+     */
+    private static final String PRELOADED_CLASS_FILE = "frameworks/base/preloaded-classes";
+    
+    /**
+     * The internal process name of the system process.  Note, this also shows up as
+     * "system_process", e.g. in ddms.
+     */
+    private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server";
+
+    /** 
+     * Names of non-application processes - these will not be checked for preloaded classes.
+     * 
+     * TODO: Replace this hardcoded list with a walk up the parent chain looking for zygote.
+     */
+    private static final Set<String> NOT_FROM_ZYGOTE = new HashSet<String>(Arrays.asList(
+            "zygote",
+            "dexopt",
+            "unknown",
+            SYSTEM_SERVER_PROCESS_NAME,
+            "com.android.development",
+            "app_process" // am & other shell commands
+    ));
+
+    /** 
+     * Long running services.  These are restricted in their contribution to the preloader
+     * because their launch time is less critical.
+     */
+    private static final Set<String> SERVICES = new HashSet<String>(Arrays.asList(
+            SYSTEM_SERVER_PROCESS_NAME,
+            "com.android.acore",
+         // Commented out to make sure DefaultTimeZones gets preloaded.
+         // "com.android.phone",
+            "com.google.process.content",
+            "android.process.media"
+    ));
+
+    /**
+     * Classes which we shouldn't load from the Zygote.
+     */
+    private static final Set<String> EXCLUDED_CLASSES = new HashSet<String>(Arrays.asList(
+        // Binders
+        "android.app.AlarmManager",
+        "android.app.SearchManager",
+        "android.os.FileObserver",
+        "com.android.server.PackageManagerService$AppDirObserver",
+
+        // Threads
+        "android.os.AsyncTask",
+        "android.pim.ContactsAsyncHelper",
+        "java.lang.ProcessManager"
+        
+    ));
+
+    /**
+     * No constructor - use static methods only
+     */
+    private Policy() {}
+    
+    /**
+     * Returns the path/file name of the preloaded classes file that will be written 
+     * by WritePreloadedClassFile.
+     */
+    public static String getPreloadedClassFileName() {
+        return PRELOADED_CLASS_FILE;
+    }
+    
+    /**
+     * Reports if a given process name was created from zygote
+     */
+    public static boolean isFromZygote(String processName) {
+        return !NOT_FROM_ZYGOTE.contains(processName);
+    }
+    
+    /**
+     * Reports if the given process name is a "long running" process or service
+     */
+    public static boolean isService(String processName) {
+        return SERVICES.contains(processName);
+    }
+    
+    /**
+     * Reports if the given class should never be preloaded
+     */
+    public static boolean isPreloadableClass(String className) {
+        return !EXCLUDED_CLASSES.contains(className);
+    }
+}
diff --git a/tools/preload/PrintCsv.java b/tools/preload/PrintCsv.java
new file mode 100644
index 0000000..9f2a318
--- /dev/null
+++ b/tools/preload/PrintCsv.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.BufferedInputStream;
+
+/**
+ * Prints raw information in CSV format.
+ */
+public class PrintCsv {
+
+    public static void main(String[] args)
+            throws IOException, ClassNotFoundException {
+        if (args.length != 1) {
+            System.err.println("Usage: PrintCsv [compiled log file]");
+            System.exit(0);
+        }
+
+        Root root = Root.fromFile(args[0]);
+
+        System.out.println("Name"
+                + ",Preloaded"
+                + ",Median Load Time (us)"
+                + ",Median Init Time (us)"
+                + ",Load Count"
+                + ",Init Count"
+                + ",Managed Heap (B)"
+                + ",Native Heap (B)"
+                + ",Managed Pages (kB)"
+                + ",Native Pages (kB)"
+                + ",Other Pages (kB)");
+
+        MemoryUsage baseline = root.baseline;
+
+        for (LoadedClass loadedClass : root.loadedClasses.values()) {
+            if (!loadedClass.systemClass) {
+                continue;
+            }
+
+            System.out.print(loadedClass.name);
+            System.out.print(',');
+            System.out.print(loadedClass.preloaded);
+            System.out.print(',');
+            System.out.print(loadedClass.medianLoadTimeMicros());
+            System.out.print(',');
+            System.out.print(loadedClass.medianInitTimeMicros());
+            System.out.print(',');
+            System.out.print(loadedClass.loads.size());
+            System.out.print(',');
+            System.out.print(loadedClass.initializations.size());
+
+            if (loadedClass.memoryUsage.isAvailable()) {
+                MemoryUsage subtracted
+                        = loadedClass.memoryUsage.subtract(baseline);
+
+                System.out.print(',');
+                System.out.print(subtracted.javaHeapSize());
+                System.out.print(',');
+                System.out.print(subtracted.nativeHeapSize);
+                System.out.print(',');
+                System.out.print(subtracted.javaPagesInK());
+                System.out.print(',');
+                System.out.print(subtracted.nativePagesInK());
+                System.out.print(',');
+                System.out.print(subtracted.otherPagesInK());
+
+            } else {
+                System.out.print(",n/a,n/a,n/a,n/a,n/a");
+            }
+
+            System.out.println();
+        }
+    }
+}
diff --git a/tools/preload/PrintPsTree.java b/tools/preload/PrintPsTree.java
new file mode 100644
index 0000000..22701fa
--- /dev/null
+++ b/tools/preload/PrintPsTree.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.BufferedInputStream;
+
+/**
+ * Prints raw information in CSV format.
+ */
+public class PrintPsTree {
+
+    public static void main(String[] args)
+            throws IOException, ClassNotFoundException {
+        if (args.length != 1) {
+            System.err.println("Usage: PrintCsv [compiled log file]");
+            System.exit(0);
+        }
+
+        FileInputStream fin = new FileInputStream(args[0]);
+        ObjectInputStream oin = new ObjectInputStream(
+                new BufferedInputStream(fin));
+
+        Root root = (Root) oin.readObject();
+
+        for (Proc proc : root.processes.values()) {
+            if (proc.parent == null) {
+                proc.print();                                
+            }
+        }
+    }
+}
diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java
new file mode 100644
index 0000000..22697f8
--- /dev/null
+++ b/tools/preload/Proc.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.TreeSet;
+import java.io.Serializable;
+
+/**
+ * A Dalvik process.
+ */
+class Proc implements Serializable {
+
+    private static final long serialVersionUID = 0;
+
+    /**
+     * Default percentage of time to cut off of app class loading times.
+     */
+    static final int PERCENTAGE_TO_PRELOAD = 75;
+
+    /**
+     * Maximum number of classes to preload for a given process.
+     */
+    static final int MAX_TO_PRELOAD = 100;
+
+    /** Parent process. */
+    final Proc parent;
+
+    /** Process ID. */
+    final int id;
+
+    /**
+     * Name of this process. We may not have the correct name at first, i.e.
+     * some classes could have been loaded before the process name was set.
+     */
+    String name;
+
+    /** Child processes. */
+    final List<Proc> children = new ArrayList<Proc>();
+
+    /** Maps thread ID to operation stack. */
+    transient final Map<Integer, LinkedList<Operation>> stacks
+            = new HashMap<Integer, LinkedList<Operation>>();
+
+    /** Number of operations. */
+    int operationCount;
+
+    /** Sequential list of operations that happened in this process. */
+    final List<Operation> operations = new ArrayList<Operation>();
+
+    /** List of past process names. */
+    final List<String> nameHistory = new ArrayList<String>();
+
+    /** Constructs a new process. */
+    Proc(Proc parent, int id) {
+        this.parent = parent;
+        this.id = id;
+    }
+
+    /** Sets name of this process. */
+    void setName(String name) {
+        if (!name.equals(this.name)) {
+            if (this.name != null) {
+                nameHistory.add(this.name);
+            }
+            this.name = name;
+        }
+    }
+
+    /**
+     * Returns the percentage of time we should cut by preloading for this
+     * app.
+     */
+    int percentageToPreload() {
+        return PERCENTAGE_TO_PRELOAD;
+    }
+
+    /**
+     * Returns a list of classes which should be preloaded.
+     * 
+     * @param takeAllClasses forces all classes to be taken (irrespective of ranking)
+     */
+    List<LoadedClass> highestRankedClasses(boolean takeAllClasses) {
+        if (!isApplication()) {
+            return Collections.emptyList();
+        }
+
+        // Sort by rank.
+        Operation[] ranked = new Operation[operations.size()];
+        ranked = operations.toArray(ranked);
+        Arrays.sort(ranked, new ClassRank());
+
+        // The percentage of time to save by preloading.
+        int timeToSave = totalTimeMicros() * percentageToPreload() / 100;
+        int timeSaved = 0;
+
+        boolean service = Policy.isService(this.name);
+
+        List<LoadedClass> highest = new ArrayList<LoadedClass>();
+        for (Operation operation : ranked) {
+            
+            // These are actual ranking decisions, which can be overridden
+            if (!takeAllClasses) {
+                if (highest.size() >= MAX_TO_PRELOAD) {
+                    System.out.println(name + " got " 
+                            + (timeSaved * 100 / timeToSave) + "% through");
+                    break;
+                }
+    
+                if (timeSaved >= timeToSave) {
+                    break;
+                }
+            }
+
+            // The remaining rules apply even to wired-down processes
+            if (!Policy.isPreloadableClass(operation.loadedClass.name)) {
+                continue;
+            }
+            
+            if (!operation.loadedClass.systemClass) {
+                continue;
+            }
+
+            // Only load java.* class for services.
+            if (!service || operation.loadedClass.name.startsWith("java.")) {
+                highest.add(operation.loadedClass);
+            }
+
+            // For services, still count the time even if it's not in java.* 
+            timeSaved += operation.medianExclusiveTimeMicros();
+        }
+
+        return highest;
+    }
+
+    /**
+     * Total time spent class loading and initializing.
+     */
+    int totalTimeMicros() {
+        int totalTime = 0;
+        for (Operation operation : operations) {
+            totalTime += operation.medianExclusiveTimeMicros();
+        }
+        return totalTime;
+    }
+
+    /** 
+     * Returns true if this process is an app.
+     *      
+     * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote.
+     */
+    public boolean isApplication() {
+        return Policy.isFromZygote(name);
+    }
+
+    /**
+     * Starts an operation.
+     *
+     * @param threadId thread the operation started in
+     * @param loadedClass class operation happened to
+     * @param time the operation started
+     */
+    void startOperation(int threadId, LoadedClass loadedClass, long time,
+            Operation.Type type) {
+        Operation o = new Operation(
+                this, loadedClass, time, operationCount++, type);
+        operations.add(o);
+
+        LinkedList<Operation> stack = stacks.get(threadId);
+        if (stack == null) {
+            stack = new LinkedList<Operation>();
+            stacks.put(threadId, stack);
+        }
+
+        if (!stack.isEmpty()) {
+            stack.getLast().subops.add(o);
+        }
+
+        stack.add(o);
+    }
+
+    /**
+     * Ends an operation.
+     *
+     * @param threadId thread the operation ended in
+     * @param loadedClass class operation happened to
+     * @param time the operation ended
+     */
+    Operation endOperation(int threadId, String className,
+            LoadedClass loadedClass, long time) {
+        LinkedList<Operation> stack = stacks.get(threadId);
+
+        if (stack == null || stack.isEmpty()) {
+            didNotStart(className);
+            return null;
+        }
+
+        Operation o = stack.getLast();
+        if (loadedClass != o.loadedClass) {
+            didNotStart(className);
+            return null;
+        }
+
+        stack.removeLast();
+
+        o.endTimeNanos = time;
+        return o;
+    }
+
+    /**
+     * Prints an error indicating that we saw the end of an operation but not
+     * the start. A bug in the logging framework which results in dropped logs
+     * causes this.
+     */
+    private static void didNotStart(String name) {
+        System.err.println("Warning: An operation ended on " + name
+            + " but it never started!");
+    }
+
+    /**
+     * Prints this process tree to stdout.
+     */
+    void print() {
+        print("");
+    }
+
+    /**
+     * Prints a child proc to standard out.
+     */
+    private void print(String prefix) {
+        System.out.println(prefix + "id=" + id + ", name=" + name);
+        for (Proc child : children) {
+            child.print(prefix + "    ");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/tools/preload/Record.java b/tools/preload/Record.java
new file mode 100644
index 0000000..b2be4d4
--- /dev/null
+++ b/tools/preload/Record.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * One line from the loaded-classes file.
+ */
+class Record {
+
+    enum Type {
+        /** Start of initialization. */
+        START_LOAD,
+
+        /** End of initialization. */
+        END_LOAD,
+
+        /** Start of initialization. */
+        START_INIT,
+
+        /** End of initialization. */
+        END_INIT
+    }
+
+    /** Parent process ID. */
+    final int ppid;
+
+    /** Process ID. */
+    final int pid;
+
+    /** Thread ID. */
+    final int tid;
+
+    /** Process name. */
+    final String processName;
+
+    /** Class loader pointer. */
+    final int classLoader;
+
+    /** Type of record. */
+    final Type type;
+
+    /** Name of loaded class. */
+    final String className;
+
+    /** Record time (ns). */
+    final long time;
+    
+    /** Source file line# */
+    int sourceLineNumber;
+
+    /**
+     * Parses a line from the loaded-classes file.
+     */
+    Record(String line, int lineNum) {
+        char typeChar = line.charAt(0);
+        switch (typeChar) {
+            case '>': type = Type.START_LOAD; break;
+            case '<': type = Type.END_LOAD; break;
+            case '+': type = Type.START_INIT; break;
+            case '-': type = Type.END_INIT; break;
+            default: throw new AssertionError("Bad line: " + line);
+        }
+
+        sourceLineNumber = lineNum;
+        
+        line = line.substring(1);
+        String[] parts = line.split(":");
+
+        ppid = Integer.parseInt(parts[0]);
+        pid = Integer.parseInt(parts[1]);
+        tid = Integer.parseInt(parts[2]);
+
+        processName = decode(parts[3]).intern();
+
+        classLoader = Integer.parseInt(parts[4]);
+        className = vmTypeToLanguage(decode(parts[5])).intern();
+
+        time = Long.parseLong(parts[6]);
+    }
+    
+    /**
+     * Decode any escaping that may have been written to the log line.
+     * 
+     * Supports unicode-style escaping:  \\uXXXX = character in hex
+     * 
+     * @param rawField the field as it was written into the log
+     * @result the same field with any escaped characters replaced
+     */
+    String decode(String rawField) {
+        String result = rawField;
+        int offset = result.indexOf("\\u");
+        while (offset >= 0) {
+            String before = result.substring(0, offset);
+            String escaped = result.substring(offset+2, offset+6);
+            String after = result.substring(offset+6);
+            
+            result = String.format("%s%c%s", before, Integer.parseInt(escaped, 16), after);
+
+            // find another but don't recurse  
+            offset = result.indexOf("\\u", offset + 1);          
+        }
+        return result;
+    }
+
+    /**
+     * Converts a VM-style name to a language-style name.
+     */
+    String vmTypeToLanguage(String typeName) {
+        // if the typename is (null), just return it as-is.  This is probably in dexopt and 
+        // will be discarded anyway.  NOTE: This corresponds to the case in dalvik/vm/oo/Class.c
+        // where dvmLinkClass() returns false and we clean up and exit.
+        if ("(null)".equals(typeName)) {
+            return typeName;
+        }
+        
+        if (!typeName.startsWith("L") || !typeName.endsWith(";") ) {
+            throw new AssertionError("Bad name: " + typeName + " in line " + sourceLineNumber);
+        }
+
+        typeName = typeName.substring(1, typeName.length() - 1);
+        return typeName.replace("/", ".");
+    }
+}
diff --git a/tools/preload/Root.java b/tools/preload/Root.java
new file mode 100644
index 0000000..949f9b7
--- /dev/null
+++ b/tools/preload/Root.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.BufferedInputStream;
+import java.io.ObjectOutputStream;
+import java.io.BufferedOutputStream;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Arrays;
+import java.nio.charset.Charset;
+
+/**
+ * Root of our data model.
+ */
+public class Root implements Serializable {
+
+    private static final long serialVersionUID = 0;
+
+    /** pid -> Proc */
+    final Map<Integer, Proc> processes = new HashMap<Integer, Proc>();
+
+    /** Class name -> LoadedClass */
+    final Map<String, LoadedClass> loadedClasses
+            = new HashMap<String, LoadedClass>();
+
+    final MemoryUsage baseline = MemoryUsage.baseline();
+
+    /**
+     * Records class loads and initializations.
+     */
+    void indexClassOperation(Record record) {
+        Proc process = processes.get(record.pid);
+
+        // Ignore dexopt output. It loads applications classes through the
+        // system class loader and messes us up.
+        if (record.processName.equals("dexopt")) {
+            return;
+        }
+
+        String name = record.className;
+        LoadedClass loadedClass = loadedClasses.get(name);
+        Operation o = null;
+
+        switch (record.type) {
+            case START_LOAD:
+            case START_INIT:
+                if (loadedClass == null) {
+                    loadedClass = new LoadedClass(
+                            name, record.classLoader == 0);
+                    if (loadedClass.systemClass) {
+                        // Only measure memory for classes in the boot
+                        // classpath.
+                        loadedClass.measureMemoryUsage();
+                    }
+                    loadedClasses.put(name, loadedClass);
+                }
+                break;
+
+            case END_LOAD:
+            case END_INIT:
+                o = process.endOperation(record.tid, record.className,
+                        loadedClass, record.time);
+                if (o == null) {
+                    return;
+                }
+        }
+
+        switch (record.type) {
+            case START_LOAD:
+                process.startOperation(record.tid, loadedClass, record.time,
+                        Operation.Type.LOAD);
+                break;
+
+            case START_INIT:
+                process.startOperation(record.tid, loadedClass, record.time,
+                        Operation.Type.INIT);
+                break;
+
+            case END_LOAD:
+                loadedClass.loads.add(o);
+                break;
+
+            case END_INIT:
+                loadedClass.initializations.add(o);
+                break;
+        }
+    }
+
+    /**
+     * Indexes information about the process from the given record.
+     */
+    void indexProcess(Record record) {
+        Proc proc = processes.get(record.pid);
+
+        if (proc == null) {
+            // Create a new process object.
+            Proc parent = processes.get(record.ppid);
+            proc = new Proc(parent, record.pid);
+            processes.put(proc.id, proc);
+            if (parent != null) {
+                parent.children.add(proc);
+            }
+        }
+
+        proc.setName(record.processName);
+    }
+
+    /**
+     * Writes this graph to a file.
+     */
+    void toFile(String fileName) throws IOException {
+        FileOutputStream out = new FileOutputStream(fileName);
+        ObjectOutputStream oout = new ObjectOutputStream(
+                new BufferedOutputStream(out));
+
+        System.err.println("Writing object model...");
+
+        oout.writeObject(this);
+
+        oout.close();
+
+        System.err.println("Done!");
+    }
+
+    /**
+     * Reads Root from a file.
+     */
+    static Root fromFile(String fileName)
+            throws IOException, ClassNotFoundException {
+        FileInputStream fin = new FileInputStream(fileName);
+        ObjectInputStream oin = new ObjectInputStream(
+                new BufferedInputStream(fin));
+
+        Root root = (Root) oin.readObject();
+
+        oin.close();
+
+        return root;
+    }
+}
diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java
new file mode 100644
index 0000000..d87b1f0
--- /dev/null
+++ b/tools/preload/WritePreloadedClassFile.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Writes /frameworks/base/preloaded-classes. Also updates LoadedClass.preloaded
+ * fields and writes over compiled log file.
+ */
+public class WritePreloadedClassFile {
+
+    public static void main(String[] args) throws IOException, ClassNotFoundException {
+        
+        // Process command-line arguments first
+        List<String> wiredProcesses = new ArrayList<String>();
+        String inputFileName = null;
+        int argOffset = 0;
+        try {
+            while ("--preload-all-process".equals(args[argOffset])) {
+                argOffset++;
+                wiredProcesses.add(args[argOffset++]);
+            }
+            
+            inputFileName = args[argOffset++];
+        } catch (RuntimeException e) {
+            System.err.println("Usage: WritePreloadedClassFile " +
+                    "[--preload-all-process process-name] " +
+                    "[compiled log file]");
+            System.exit(0);
+        }
+
+        Root root = Root.fromFile(inputFileName);
+
+        for (LoadedClass loadedClass : root.loadedClasses.values()) {
+            loadedClass.preloaded = false;
+        }
+
+        Writer out = new BufferedWriter(new OutputStreamWriter(
+                new FileOutputStream(Policy.getPreloadedClassFileName()),
+                Charset.forName("US-ASCII")));
+
+        out.write("# Classes which are preloaded by com.android.internal.os.ZygoteInit.\n");
+        out.write("# Automatically generated by /frameworks/base/tools/preload.\n");
+        out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight="
+                + ClassRank.SEQUENCE_WEIGHT
+                + ", bucket_size=" + ClassRank.BUCKET_SIZE
+                + "\n");
+        for (String wiredProcess : wiredProcesses) {
+            out.write("# forcing classes loaded by: " + wiredProcess + "\n");
+        }
+
+        Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>();
+        for (Proc proc : root.processes.values()) {
+            // test to see if this is one of the wired-down ("take all classes") processes
+            boolean isWired = wiredProcesses.contains(proc.name);
+            
+            List<LoadedClass> highestForProc = proc.highestRankedClasses(isWired);
+
+            System.out.println(proc.name + ": " + highestForProc.size());
+
+            for (LoadedClass loadedClass : highestForProc) {
+                loadedClass.preloaded = true;
+            }
+            highestRanked.addAll(highestForProc);
+        }
+
+        for (LoadedClass loadedClass : highestRanked) {
+            out.write(loadedClass.name);
+            out.write('\n');
+        }
+
+        out.close();
+
+        System.out.println(highestRanked.size()
+                + " classes will be preloaded.");
+
+        // Update data to reflect LoadedClass.preloaded changes.
+        root.toFile(inputFileName);
+    }
+}
diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk
new file mode 100644
index 0000000..435699d
--- /dev/null
+++ b/tools/preload/loadclass/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE := loadclass
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/tools/preload/loadclass/LoadClass.java b/tools/preload/loadclass/LoadClass.java
new file mode 100644
index 0000000..471cc84
--- /dev/null
+++ b/tools/preload/loadclass/LoadClass.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.util.Log;
+import android.os.Debug;
+
+/**
+ * Loads a class, runs the garbage collector, and prints showmap output.
+ *
+ * <p>Usage: dalvikvm LoadClass [class name]
+ */
+class LoadClass {
+
+    public static void main(String[] args) {
+        System.loadLibrary("android_runtime");
+
+        if (registerNatives() < 0) {
+            throw new RuntimeException("Error registering natives.");    
+        }
+
+        Debug.startAllocCounting();
+
+        if (args.length > 0) {
+            try {
+                Class.forName(args[0]);
+            } catch (ClassNotFoundException e) {
+                Log.w("LoadClass", e);
+                return;
+            }
+        }
+
+        System.gc();
+
+        int allocCount = Debug.getGlobalAllocCount();
+        int allocSize = Debug.getGlobalAllocSize();
+        int freedCount = Debug.getGlobalFreedCount();
+        int freedSize = Debug.getGlobalFreedSize();
+        long nativeHeapSize = Debug.getNativeHeapSize();
+
+        Debug.stopAllocCounting();
+
+        StringBuilder response = new StringBuilder("DECAFBAD");
+
+        int[] pages = new int[6];
+        Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
+        Debug.getMemoryInfo(memoryInfo);
+        response.append(',').append(memoryInfo.nativeSharedDirty);
+        response.append(',').append(memoryInfo.dalvikSharedDirty);
+        response.append(',').append(memoryInfo.otherSharedDirty);
+        response.append(',').append(memoryInfo.nativePrivateDirty);
+        response.append(',').append(memoryInfo.dalvikPrivateDirty);
+        response.append(',').append(memoryInfo.otherPrivateDirty);
+
+        response.append(',').append(allocCount);
+        response.append(',').append(allocSize);
+        response.append(',').append(freedCount);
+        response.append(',').append(freedSize);
+        response.append(',').append(nativeHeapSize);
+        
+        System.out.println(response.toString());
+    }
+
+    /**
+     * Registers native functions. See AndroidRuntime.cpp.
+     */
+    static native int registerNatives();
+}
diff --git a/tools/preload/preload.iml b/tools/preload/preload.iml
new file mode 100644
index 0000000..d1fab57
--- /dev/null
+++ b/tools/preload/preload.iml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file:///tmp/preload/" />
+    <exclude-output />
+    <output-test url="file:///tmp/preload/" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntryProperties />
+  </component>
+</module>
+
diff --git a/tools/preload/preload.ipr b/tools/preload/preload.ipr
new file mode 100644
index 0000000..c5613ad
--- /dev/null
+++ b/tools/preload/preload.ipr
@@ -0,0 +1,467 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project relativePaths="false" version="4">
+  <component name="AntConfiguration">
+    <defaultAnt bundledAnt="true" />
+  </component>
+  <component name="BuildJarProjectSettings">
+    <option name="BUILD_JARS_ON_MAKE" value="false" />
+  </component>
+  <component name="ChangeBrowserSettings">
+    <option name="MAIN_SPLITTER_PROPORTION" value="0.3" />
+    <option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" />
+    <option name="USE_DATE_BEFORE_FILTER" value="false" />
+    <option name="USE_DATE_AFTER_FILTER" value="false" />
+    <option name="USE_CHANGE_BEFORE_FILTER" value="false" />
+    <option name="USE_CHANGE_AFTER_FILTER" value="false" />
+    <option name="DATE_BEFORE" value="" />
+    <option name="DATE_AFTER" value="" />
+    <option name="CHANGE_BEFORE" value="" />
+    <option name="CHANGE_AFTER" value="" />
+    <option name="USE_USER_FILTER" value="false" />
+    <option name="USER" value="" />
+  </component>
+  <component name="CodeStyleProjectProfileManger">
+    <option name="PROJECT_PROFILE" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+  </component>
+  <component name="CodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS" />
+    <option name="USE_PER_PROJECT_SETTINGS" value="false" />
+  </component>
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <option name="DEPLOY_AFTER_MAKE" value="0" />
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)" />
+      <entry name=".+\.(gif|png|jpeg|jpg)" />
+    </resourceExtensions>
+    <wildcardResourcePatterns>
+      <entry name="?*.properties" />
+      <entry name="?*.xml" />
+      <entry name="?*.gif" />
+      <entry name="?*.png" />
+      <entry name="?*.jpeg" />
+      <entry name="?*.jpg" />
+      <entry name="?*.html" />
+      <entry name="?*.dtd" />
+      <entry name="?*.tld" />
+    </wildcardResourcePatterns>
+  </component>
+  <component name="Cvs2Configuration">
+    <option name="PRUNE_EMPTY_DIRECTORIES" value="true" />
+    <option name="MERGING_MODE" value="0" />
+    <option name="MERGE_WITH_BRANCH1_NAME" value="HEAD" />
+    <option name="MERGE_WITH_BRANCH2_NAME" value="HEAD" />
+    <option name="RESET_STICKY" value="false" />
+    <option name="CREATE_NEW_DIRECTORIES" value="true" />
+    <option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv" />
+    <option name="PROCESS_UNKNOWN_FILES" value="false" />
+    <option name="PROCESS_DELETED_FILES" value="false" />
+    <option name="PROCESS_IGNORED_FILES" value="false" />
+    <option name="RESERVED_EDIT" value="false" />
+    <option name="CHECKOUT_DATE_OR_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="UPDATE_DATE_OR_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="SHOW_CHANGES_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="SHOW_OUTPUT" value="false" />
+    <option name="ADD_WATCH_INDEX" value="0" />
+    <option name="REMOVE_WATCH_INDEX" value="0" />
+    <option name="UPDATE_KEYWORD_SUBSTITUTION" />
+    <option name="MAKE_NEW_FILES_READONLY" value="false" />
+    <option name="SHOW_CORRUPTED_PROJECT_FILES" value="0" />
+    <option name="TAG_AFTER_PROJECT_COMMIT" value="false" />
+    <option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="true" />
+    <option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="" />
+    <option name="CLEAN_COPY" value="false" />
+  </component>
+  <component name="DependenciesAnalyzeManager">
+    <option name="myForwardDirection" value="false" />
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </component>
+  <component name="EclipseCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EclipseEmbeddedCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="ExportToHTMLSettings">
+    <option name="PRINT_LINE_NUMBERS" value="false" />
+    <option name="OPEN_IN_BROWSER" value="false" />
+    <option name="OUTPUT_DIRECTORY" />
+  </component>
+  <component name="IdProvider" IDEtalkID="D171F99B9178C1675593DC9A76A5CC7E" />
+  <component name="InspectionProjectProfileManager">
+    <option name="PROJECT_PROFILE" value="Project Default" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+    <scopes />
+    <profiles>
+      <profile version="1.0" is_locked="false">
+        <option name="myName" value="Project Default" />
+        <option name="myLocal" value="false" />
+        <inspection_tool class="JavaDoc" level="WARNING" enabled="false">
+          <option name="TOP_LEVEL_CLASS_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="INNER_CLASS_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="METHOD_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
+            </value>
+          </option>
+          <option name="FIELD_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="IGNORE_DEPRECATED" value="false" />
+          <option name="IGNORE_JAVADOC_PERIOD" value="true" />
+          <option name="myAdditionalJavadocTags" value="" />
+        </inspection_tool>
+        <inspection_tool class="OnDemandImport" level="WARNING" enabled="true" />
+        <inspection_tool class="SamePackageImport" level="WARNING" enabled="true" />
+        <inspection_tool class="JavaLangImport" level="WARNING" enabled="true" />
+        <inspection_tool class="RedundantImport" level="WARNING" enabled="true" />
+        <inspection_tool class="UnusedImport" level="WARNING" enabled="true" />
+      </profile>
+    </profiles>
+    <list size="0" />
+  </component>
+  <component name="JavacSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="DEPRECATION" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY" />
+    <option name="OPTION_SCOPE" value="protected" />
+    <option name="OPTION_HIERARCHY" value="true" />
+    <option name="OPTION_NAVIGATOR" value="true" />
+    <option name="OPTION_INDEX" value="true" />
+    <option name="OPTION_SEPARATE_INDEX" value="true" />
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+    <option name="OPTION_DEPRECATED_LIST" value="true" />
+    <option name="OTHER_OPTIONS" value="" />
+    <option name="HEAP_SIZE" />
+    <option name="LOCALE" />
+    <option name="OPEN_IN_BROWSER" value="true" />
+  </component>
+  <component name="JikesSettings">
+    <option name="JIKES_PATH" value="" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="DEPRECATION" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="IS_EMACS_ERRORS_MODE" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="LogConsolePreferences">
+    <option name="FILTER_ERRORS" value="false" />
+    <option name="FILTER_WARNINGS" value="false" />
+    <option name="FILTER_INFO" value="true" />
+    <option name="CUSTOM_FILTER" />
+  </component>
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+  <component name="PerforceChangeBrowserSettings">
+    <option name="USE_CLIENT_FILTER" value="true" />
+    <option name="CLIENT" value="" />
+  </component>
+  <component name="ProjectFileVersion" converted="true" />
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/preload.iml" filepath="$PROJECT_DIR$/preload.iml" />
+    </modules>
+  </component>
+  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK">
+    <output url="file:///tmp/preload" />
+  </component>
+  <component name="RmicSettings">
+    <option name="IS_EANABLED" value="false" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="GENERATE_IIOP_STUBS" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="StarteamConfiguration">
+    <option name="SERVER" value="" />
+    <option name="PORT" value="49201" />
+    <option name="USER" value="" />
+    <option name="PASSWORD" value="" />
+    <option name="PROJECT" value="" />
+    <option name="VIEW" value="" />
+    <option name="ALTERNATIVE_WORKING_PATH" value="" />
+    <option name="LOCK_ON_CHECKOUT" value="false" />
+    <option name="UNLOCK_ON_CHECKIN" value="false" />
+  </component>
+  <component name="Struts Assistant">
+    <option name="showInputs" value="true" />
+    <option name="resources">
+      <value>
+        <option name="strutsPath" />
+        <option name="strutsHelp" />
+      </value>
+    </option>
+    <option name="selectedTaglibs" />
+    <option name="selectedTaglibs" />
+    <option name="myStrutsValidationEnabled" value="true" />
+    <option name="myTilesValidationEnabled" value="true" />
+    <option name="myValidatorValidationEnabled" value="true" />
+    <option name="myReportErrorsAsWarnings" value="true" />
+  </component>
+  <component name="SvnChangesBrowserSettings">
+    <option name="USE_AUTHOR_FIELD" value="true" />
+    <option name="AUTHOR" value="" />
+    <option name="LOCATION" value="" />
+    <option name="USE_PROJECT_SETTINGS" value="true" />
+    <option name="USE_ALTERNATE_LOCATION" value="false" />
+  </component>
+  <component name="SvnConfiguration">
+    <option name="USER" value="" />
+    <option name="PASSWORD" value="" />
+    <option name="PROCESS_UNRESOLVED" value="false" />
+    <option name="LAST_MERGED_REVISION" />
+    <option name="UPDATE_RUN_STATUS" value="false" />
+    <option name="UPDATE_RECURSIVELY" value="true" />
+    <option name="MERGE_DRY_RUN" value="false" />
+  </component>
+  <component name="VCS.FileViewConfiguration">
+    <option name="SELECTED_STATUSES" value="DEFAULT" />
+    <option name="SELECTED_COLUMNS" value="DEFAULT" />
+    <option name="SHOW_FILTERS" value="true" />
+    <option name="CUSTOMIZE_VIEW" value="true" />
+    <option name="SHOW_FILE_HISTORY_AS_TREE" value="true" />
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Perforce" />
+  </component>
+  <component name="VssConfiguration">
+    <option name="CLIENT_PATH" value="" />
+    <option name="SRCSAFEINI_PATH" value="" />
+    <option name="USER_NAME" value="" />
+    <option name="PWD" value="" />
+    <option name="VSS_IS_INITIALIZED" value="false" />
+    <CheckoutOptions>
+      <option name="COMMENT" value="" />
+      <option name="DO_NOT_GET_LATEST_VERSION" value="false" />
+      <option name="REPLACE_WRITABLE" value="false" />
+      <option name="RECURSIVE" value="false" />
+    </CheckoutOptions>
+    <CheckinOptions>
+      <option name="COMMENT" value="" />
+      <option name="KEEP_CHECKED_OUT" value="false" />
+      <option name="RECURSIVE" value="false" />
+    </CheckinOptions>
+    <AddOptions>
+      <option name="STORE_ONLY_LATEST_VERSION" value="false" />
+      <option name="CHECK_OUT_IMMEDIATELY" value="false" />
+      <option name="FILE_TYPE" value="0" />
+    </AddOptions>
+    <UndocheckoutOptions>
+      <option name="MAKE_WRITABLE" value="false" />
+      <option name="REPLACE_LOCAL_COPY" value="0" />
+      <option name="RECURSIVE" value="false" />
+    </UndocheckoutOptions>
+    <GetOptions>
+      <option name="REPLACE_WRITABLE" value="0" />
+      <option name="MAKE_WRITABLE" value="false" />
+      <option name="ANSWER_NEGATIVELY" value="false" />
+      <option name="ANSWER_POSITIVELY" value="false" />
+      <option name="RECURSIVE" value="false" />
+      <option name="VERSION" />
+    </GetOptions>
+    <VssConfigurableExcludedFilesTag />
+  </component>
+  <component name="antWorkspaceConfiguration">
+    <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
+    <option name="FILTER_TARGETS" value="false" />
+  </component>
+  <component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1">
+    <option name="myLastEditedConfigurable" />
+  </component>
+  <component name="com.intellij.jsf.UserDefinedFacesConfigs">
+    <option name="USER_DEFINED_CONFIGS">
+      <value>
+        <list size="0" />
+      </value>
+    </option>
+  </component>
+  <component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="" version="1">
+    <option name="myPlainMode" value="false" />
+    <option name="myLastEditedConfigurable" />
+  </component>
+  <component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1">
+    <option name="myLastEditedConfigurable" />
+  </component>
+  <component name="uidesigner-configuration">
+    <option name="INSTRUMENT_CLASSES" value="true" />
+    <option name="COPY_FORMS_RUNTIME_TO_OUTPUT" value="true" />
+    <option name="DEFAULT_LAYOUT_MANAGER" value="GridLayoutManager" />
+  </component>
+</project>
+