merge in ics-release history after reset to master
diff --git a/Android.mk b/Android.mk
index 3f65320..8a8ed78 100644
--- a/Android.mk
+++ b/Android.mk
@@ -89,6 +89,7 @@
 	src/images/SkCreateRLEPixelRef.cpp \
 	src/images/SkImageDecoder_Factory.cpp \
 	src/images/SkImageEncoder_Factory.cpp \
+	src/ports/FontHostConfiguration_android.cpp \
 	src/ports/SkFontHost_android.cpp \
 	src/ports/SkFontHost_gamma.cpp \
 	src/ports/SkFontHost_FreeType.cpp \
@@ -221,7 +222,8 @@
 	libemoji \
 	libjpeg \
 	libutils \
-	libz
+	libz \
+	libexpat
 
 LOCAL_STATIC_LIBRARIES := \
 	libft2 \
@@ -243,7 +245,8 @@
 	external/giflib \
 	external/jpeg \
 	external/webp/include \
-	frameworks/opt/emoji
+	frameworks/opt/emoji \
+	external/expat/lib
 
 ifeq ($(NO_FALLBACK_FONT),true)
 	LOCAL_CFLAGS += -DNO_FALLBACK_FONT
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index 6f8dfcc..51f8a46 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -38,9 +38,6 @@
 #include "webp/encode.h"
 }
 
-/* If defined, work around missing padding byte in content generated by webpconv */
-#define WEBPCONV_MISSING_PADDING 1
-
 #ifdef ANDROID
 #include <cutils/properties.h>
 
@@ -59,24 +56,6 @@
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
-// An helper to extract a integer (little endian) from byte array. This is
-// called only once per decoding, so no real need to optimize it in any way
-static uint32_t getint32l(unsigned char *in) {
-    int result;
-    unsigned char *buffer = (unsigned char*) in;
-
-    if (buffer == NULL) {
-        return 0;
-    }
-
-    result = buffer[3];
-    result = (result << 8) + buffer[2];
-    result = (result << 8) + buffer[1];
-    result = (result << 8) + buffer[0];
-
-    return result;
-}
-
 static const size_t WEBP_VP8_HEADER_SIZE = 30;
 static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
 
@@ -152,288 +131,6 @@
     return false; // must always return false
 }
 
-typedef struct {
-    SkBitmap* image;
-    SkStream* stream;
-} WEBPImage;
-
-// WebP library embeds its own YUV to RGB converter. However, High-level API doesn't take benefit
-// of (U,v) clipped values being valid for up to 4 pixels, and so there is a significant improvement
-// in performance in handling this on our own.
-// TODO: use architecture-optimized (eventually hardware-accelerated) YUV converters
-#define YUV_HALF (1 << (YUV_FIX - 1))
-#define YUV_FIX 16                   // fixed-point precision
-#define YUV_RANGE_MIN (-227)         // min value of r/g/b output
-#define YUV_RANGE_MAX (256 + 226)    // max value of r/g/b output
-static int16_t VP8kVToR[256], VP8kUToB[256];
-static int32_t VP8kVToG[256], VP8kUToG[256];
-static uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
-
-static void yuv_init_tables() {
-    int i;
-
-    for (i = 0; i < 256; ++i) {
-        VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX;
-        VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF;
-        VP8kVToG[i] = -45773 * (i - 128);
-        VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX;
-    }
-    for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) {
-        const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX;
-        VP8kClip[i - YUV_RANGE_MIN] = (k < 0) ? 0 : (k > 255) ? 255 : k;
-    }
-}
-
-// Static global mutex to protect Webp initialization
-static SkMutex gYUVMutex;
-static bool gYUVReady = false;
-
-static bool yuv_init() {
-    if (!gYUVReady) {
-        gYUVMutex.acquire();
-        if (!gYUVReady) {
-            yuv_init_tables();
-            gYUVReady = true;
-        }
-        gYUVMutex.release();
-    }
-
-    return gYUVReady;
-}
-
-#define PutRGBA(p,r,g,b) (((SkPMColor*) (p))[0] = SkPackARGB32(0xff,(r),(g),(b)))
-#define PutRGB565(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackRGB16((r)>>3,(g)>>2,(b)>>3))
-#define PutRGBA4444(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackARGB4444(0xf,(r)>>4,(g)>>4,(b)>>4))
-
-#define CRGBA(p,y,roff,goff,boff) PutRGBA(p,         \
-          VP8kClip[(y) + (roff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (goff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (boff) - YUV_RANGE_MIN])
-#define CRGB565(p,y,roff,goff,boff) PutRGB565(p,     \
-          VP8kClip[(y) + (roff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (goff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (boff) - YUV_RANGE_MIN])
-#define CRGBA4444(p,y,roff,goff,boff) PutRGBA4444(p, \
-          VP8kClip[(y) + (roff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (goff) - YUV_RANGE_MIN],    \
-          VP8kClip[(y) + (boff) - YUV_RANGE_MIN])
-
-static int block_put(const VP8Io* io) {
-    WEBPImage *p = (WEBPImage*) io->opaque;
-    SkBitmap* decodedBitmap = p->image;
-
-    const int w = io->width;
-    const int mb_h = io->mb_h;
-
-    const uint8_t *y, *y2, *u, *v;
-    const uint8_t *py, *py2, *pu, *pv;
-
-    uint8_t* pout;
-    uint8_t* pout2;
-
-    int i, j;
-    const int ystride2 = io->y_stride * 2;
-    int bpp;
-    SkBitmap::Config config = decodedBitmap->config();
-
-    //SkASSERT(!(io->mb_y & 1));
-
-    y = io->y;
-    u = io->u;
-    v = io->v;
-
-    switch (config) {
-        case SkBitmap::kARGB_8888_Config:
-            bpp = 4;
-            break;
-        case SkBitmap::kRGB_565_Config:
-            bpp = 2;
-            break;
-        case SkBitmap::kARGB_4444_Config:
-            bpp = 2;
-            break;
-        default:
-            // Unsupported config
-            return 0;
-    }
-
-    for (j = 0; j < mb_h;) {
-        pout = decodedBitmap->getAddr8(0, io->mb_y + j);
-        if (j + 1 < mb_h) {
-            y2 = y + io->y_stride;
-            pout2 = decodedBitmap->getAddr8(0, io->mb_y + j + 1);
-        } else {
-            y2 = NULL;
-            pout2 = NULL;
-        }
-
-        // Copy YUV into target buffer
-        py = y;
-        pu = u;
-        pv = v;
-
-        py2 = y2;
-
-        // Leave test for config out of inner loop. This implies some redundancy in code,
-        // but help in supporting several configs without degrading performance.
-        // As a reminder, one must *NOT* put py increment into parameters (i.e. *py++) in the hope to
-        // improve performance or code readability. Since it is used as argument of a macro which uses it
-        // several times in its expression, so this would end up in having it too much incremented
-        switch (config) {
-            case SkBitmap::kARGB_8888_Config:
-                for (i = 0; i < w; i += 2) {
-                    // U and V are common for up to 4 pixels
-                    const int r_off = VP8kVToR[*pv];
-                    const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX;
-                    const int b_off = VP8kUToB[*pu];
-
-                    CRGBA(pout, *py, r_off, g_off, b_off);
-                    pout += bpp;
-                    py++;
-
-                    // Width shouldn't be odd, so this should always be true
-                    if (i + 1 < w) {
-                        CRGBA(pout, *py, r_off, g_off, b_off);
-                        pout += bpp;
-                        py++;
-                    }
-
-                    if (pout2) {
-                        CRGBA(pout2, *py2, r_off, g_off, b_off);
-                        pout2 += bpp;
-                        py2++;
-
-                        // Width shouldn't be odd, so this should always be true
-                        if (i + 1 < w) {
-                            CRGBA(pout2, *py2, r_off, g_off, b_off);
-                            pout2 += bpp;
-                            py2++;
-                        }
-                    }
-
-                    pu++;
-                    pv++;
-                }
-                break;
-            case SkBitmap::kRGB_565_Config:
-                for (i = 0; i < w; i += 2) {
-                    // U and V are common for up to 4 pixels
-                    const int r_off = VP8kVToR[*pv];
-                    const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX;
-                    const int b_off = VP8kUToB[*pu];
-
-                    CRGB565(pout, *py, r_off, g_off, b_off);
-                    pout += bpp;
-                    py++;
-
-                    // Width shouldn't be odd, so this should always be true
-                    if (i + 1 < w) {
-                        CRGB565(pout, *py, r_off, g_off, b_off);
-                        pout += bpp;
-                        py++;
-                    }
-
-                    if (pout2) {
-                        CRGB565(pout2, *py2, r_off, g_off, b_off);
-                        pout2 += bpp;
-                        py2++;
-
-                        // Width shouldn't be odd, so this should always be true
-                        if (i + 1 < w) {
-                            CRGB565(pout2, *py2, r_off, g_off, b_off);
-                            pout2 += bpp;
-                            py2++;
-                        }
-                    }
-
-                    pu++;
-                    pv++;
-                }
-                break;
-            case SkBitmap::kARGB_4444_Config:
-                for (i = 0; i < w; i += 2) {
-                    // U and V are common for up to 4 pixels
-                    const int r_off = VP8kVToR[*pv];
-                    const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX;
-                    const int b_off = VP8kUToB[*pu];
-
-                    CRGBA4444(pout, *py, r_off, g_off, b_off);
-                    pout += bpp;
-                    py++;
-
-                    // Width shouldn't be odd, so this should always be true
-                    if (i + 1 < w) {
-                        CRGBA4444(pout, *py, r_off, g_off, b_off);
-                        pout += bpp;
-                        py++;
-                    }
-
-                    if (pout2) {
-                        CRGBA4444(pout2, *py2, r_off, g_off, b_off);
-                        pout2 += bpp;
-                        py2++;
-
-                        // Width shouldn't be odd, so this should always be true
-                        if (i + 1 < w) {
-                            CRGBA4444(pout2, *py2, r_off, g_off, b_off);
-                            pout2 += bpp;
-                            py2++;
-                        }
-                    }
-
-                    pu++;
-                    pv++;
-                }
-                break;
-            default:
-                // Unsupported config (can't happen, but prevents compiler warning)
-                SkASSERT(0);
-                break;
-        }
-
-        if (y2) {
-            // Scanned and populated two rows
-            y += ystride2;
-            y2 += ystride2;
-            j += 2;
-        } else {
-            // Skip to next row
-            y += io->y_stride;
-            j++;
-        }
-
-        u += io->uv_stride;
-        v += io->uv_stride;
-    }
-
-    return 1;
-}
-
-static int block_setup(VP8Io* io) {
-    yuv_init();
-    return 1;
-}
-
-static void block_teardown(const VP8Io* io) {
-}
-
-static bool webp_init_custom_io(WebPIDecoder* idec, SkBitmap* decodedBitmap) {
-    if (idec == NULL) {
-        return false;
-    }
-
-    WEBPImage pSrc;
-    // Custom Put callback need reference to target image.
-    pSrc.image = decodedBitmap;
-
-    if (!WebPISetIOHooks(idec, block_put, block_setup, block_teardown,
-                         (void*)&pSrc)) {
-        return false;
-    }
-
-    return true;
-}
-
 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively
 // and decodes this block to appropriate color-space as per config object.
 static bool webp_idecode(SkStream* stream, SkBitmap* decodedBitmap) {
@@ -442,13 +139,32 @@
     stream->rewind();
     const uint32_t contentSize = stream->getLength();
 
-    WebPIDecoder* idec = WebPINew(MODE_YUV);
-    if (idec == NULL) {
+    WEBP_CSP_MODE mode = MODE_LAST;
+    SkBitmap::Config config = decodedBitmap->config();
+    if (config == SkBitmap::kARGB_8888_Config) {
+      mode = MODE_RGBA;
+    } else if (config == SkBitmap::kARGB_4444_Config) {
+      mode = MODE_RGBA_4444;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+      mode = MODE_RGB_565;
+    } else {
+      return false;
+    }
+
+    WebPDecoderConfig decode_config;
+    if (WebPInitDecoderConfig(&decode_config) == 0) {
         return false;
     }
 
-    if (!webp_init_custom_io(idec, decodedBitmap)) {
-        WebPIDelete(idec);
+    decode_config.output.colorspace = mode;
+    decode_config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+    decode_config.output.u.RGBA.stride = decodedBitmap->rowBytes();
+    decode_config.output.u.RGBA.size = decodedBitmap->getSize();
+    decode_config.output.is_external_memory = 1;
+
+    WebPIDecoder* idec = WebPIDecode(NULL, NULL, &decode_config);
+    if (idec == NULL) {
+        WebPFreeDecBuffer(&decode_config.output);
         return false;
     }
 
@@ -460,6 +176,7 @@
     unsigned char* input = (uint8_t*)srcStorage.get();
     if (input == NULL) {
         WebPIDelete(idec);
+        WebPFreeDecBuffer(&decode_config.output);
         return false;
     }
 
@@ -483,6 +200,7 @@
     }
     srcStorage.free();
     WebPIDelete(idec);
+    WebPFreeDecBuffer(&decode_config.output);
 
     if (bytes_remaining > 0) {
         return false;
@@ -509,7 +227,6 @@
         }
     }
 
-
     if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
         return false;
     }
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
new file mode 100644
index 0000000..78f1de4
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -0,0 +1,192 @@
+/* libs/graphics/ports/FontHostConfiguration_android.cpp
+**
+** Copyright 2011, 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.
+*/
+
+#include "FontHostConfiguration_android.h"
+#include <expat.h>
+#include "SkTDArray.h"
+
+#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
+#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
+#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
+
+
+// These defines are used to determine the kind of tag that we're currently populating with data.
+// We only care about the sibling tags nameset and fileset for now.
+#define NO_TAG 0
+#define NAMESET_TAG 1
+#define FILESET_TAG 2
+
+/**
+ * The FamilyData structure is passed around by the parser so that each handler can read these
+ * variables that are relevant to the current parsing.
+ */
+struct FamilyData {
+    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
+            parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
+
+    XML_Parser *parser;                // The expat parser doing the work
+    SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
+    FontFamily *currentFamily;         // The current family being created
+    int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
+};
+
+/**
+ * Handler for arbitrary text. This is used to parse the text inside each name or file tag. The
+ * resulting strings are put into the fNames or fFileNames arrays.
+ */
+void textHandler(void *data, const char *s, int len) {
+    FamilyData *familyData = (FamilyData*) data;
+    // Make sure we're in the right state to store this name information
+    if (familyData->currentFamily &&
+            (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
+        // Malloc new buffer to store the string
+        char *buff;
+        buff = (char*) malloc((len + 1) * sizeof(char));
+        strncpy(buff, s, len);
+        buff[len] = '\0';
+        switch (familyData->currentTag) {
+        case NAMESET_TAG:
+            *(familyData->currentFamily->fNames.append()) = buff;
+            break;
+        case FILESET_TAG:
+            *(familyData->currentFamily->fFileNames.append()) = buff;
+            break;
+        default:
+            // Noop - don't care about any text that's not in the Fonts or Names list
+            break;
+        }
+    }
+}
+
+/**
+ * Handler for the start of a tag. The only tags we expect are family, nameset, fileset, name,
+ * and file.
+ */
+void startElementHandler(void *data, const char *tag, const char **atts) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        familyData->currentFamily = new FontFamily();
+        familyData->currentFamily->order = -1;
+        // The Family tag has an optional "order" attribute with an integer value >= 0
+        // If this attribute does not exist, the default value is -1
+        for (int i = 0; atts[i] != NULL; i += 2) {
+            const char* attribute = atts[i];
+            const char* valueString = atts[i+1];
+            int value;
+            int len = sscanf(valueString, "%d", &value);
+            if (len > 0) {
+                familyData->currentFamily->order = value;
+            }
+        }
+    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+        familyData->currentTag = NAMESET_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
+        familyData->currentTag = FILESET_TAG;
+    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // If it's a Name, parse the text inside
+        XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    }
+}
+
+/**
+ * Handler for the end of tags. We only care about family, nameset, fileset, name, and file.
+ */
+void endElementHandler(void *data, const char *tag) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        // Done parsing a Family - store the created currentFamily in the families array
+        *familyData->families.append() = familyData->currentFamily;
+        familyData->currentFamily = NULL;
+    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+        familyData->currentTag = NO_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len)== 0) {
+        familyData->currentTag = NO_TAG;
+    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // Disable the arbitrary text handler installed to load Name data
+        XML_SetCharacterDataHandler(*familyData->parser, NULL);
+    }
+}
+
+/**
+ * This function parses the given filename and stores the results in the given families array.
+ */
+void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
+    XML_Parser parser = XML_ParserCreate(NULL);
+    FamilyData *familyData = new FamilyData(&parser, families);
+    XML_SetUserData(parser, familyData);
+    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+    FILE *file = fopen(filename, "r");
+    if (file == NULL) {
+        // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
+        // are optional - failure here is okay because one of these optional files may not exist.
+        return;
+    }
+    char buffer[512];
+    bool done = false;
+    while (!done) {
+        fgets(buffer, sizeof(buffer), file);
+        int len = strlen(buffer);
+        if (feof(file) != 0) {
+            done = true;
+        }
+        XML_Parse(parser, buffer, len, done);
+    }
+}
+
+/**
+ * Loads data on font families from various expected configuration files. The resulting data
+ * is returned in the given fontFamilies array.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+
+    SkTDArray<FontFamily*> fallbackFonts;
+    SkTDArray<FontFamily*> vendorFonts;
+    parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
+    parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
+    parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
+
+    // This loop inserts the vendor fallback fonts in the correct order in the overall
+    // fallbacks list.
+    int currentOrder = -1;
+    for (int i = 0; i < vendorFonts.count(); ++i) {
+        FontFamily* family = vendorFonts[i];
+        int order = family->order;
+        if (order < 0) {
+            if (currentOrder < 0) {
+                // Default case - just add it to the end of the fallback list
+                *fallbackFonts.append() = family;
+            } else {
+                // no order specified on this font, but we're incrementing the order
+                // based on an earlier order insertion request
+                *fallbackFonts.insert(currentOrder++) = family;
+            }
+        } else {
+            // Add the font into the fallback list in the specified order. Set currentOrder
+            // for correct placement of other fonts in the vendor list.
+            *fallbackFonts.insert(order) = family;
+            currentOrder = order + 1;
+        }
+    }
+    // Append all fallback fonts to system fonts
+    for (int i = 0; i < fallbackFonts.count(); ++i) {
+        *fontFamilies.append() = fallbackFonts[i];
+    }
+}
diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h
new file mode 100644
index 0000000..57a0126
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.h
@@ -0,0 +1,41 @@
+/* libs/graphics/ports/FontHostConfiguration_android.h
+**
+** Copyright 2011, 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.
+*/
+#ifndef FONTHOSTCONFIGURATION_ANDROID_H_
+#define FONTHOSTCONFIGURATION_ANDROID_H_
+
+#include "SkTDArray.h"
+
+/**
+ * The FontFamily data structure is created during parsing and handed back to Skia to fold
+ * into its representation of font families. fNames is the list of font names that alias to a
+ * font family. fFileNames is the list of font filenames for the family. Order is the priority
+ * order for the font. This is used internally to determine the order in which to place fallback
+ * fonts as they are read from the configuration files.
+ */
+struct FontFamily {
+    SkTDArray<const char*>  fNames;
+    SkTDArray<const char*>  fFileNames;
+    int order;
+};
+
+/**
+ * Parses all system font configuration files and returns the results in an array of FontFamily
+ * structures.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
+
+#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
index 7d17544..76d9961 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -23,6 +23,7 @@
 #include "SkStream.h"
 #include "SkThread.h"
 #include "SkTSearch.h"
+#include "FontHostConfiguration_android.h"
 #include <stdio.h>
 
 #define FONT_CACHE_MEMORY_BUDGET    (768 * 1024)
@@ -395,64 +396,80 @@
     const char* const*  fNames;     // null-terminated list
 };
 
-static const char* gSansNames[] = {
-    "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
-};
-
-static const char* gSerifNames[] = {
-    "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
-    "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
-};
-
-static const char* gMonoNames[] = {
-    "monospace", "courier", "courier new", "monaco", NULL
-};
-
 // deliberately empty, but we use the address to identify fallback fonts
 static const char* gFBNames[] = { NULL };
 
-/*  Fonts must be grouped by family, with the first font in a family having the
-    list of names (even if that list is empty), and the following members having
-    null for the list. The names list must be NULL-terminated
-*/
-static const FontInitRec gSystemFonts[] = {
-    { "DroidSans.ttf",              gSansNames  },
-    { "DroidSans-Bold.ttf",         NULL        },
-    { "DroidSerif-Regular.ttf",     gSerifNames },
-    { "DroidSerif-Bold.ttf",        NULL        },
-    { "DroidSerif-Italic.ttf",      NULL        },
-    { "DroidSerif-BoldItalic.ttf",  NULL        },
-    { "DroidSansMono.ttf",          gMonoNames  },
-    /*  These are optional, and can be ignored if not found in the file system.
-        These are appended to gFallbackFonts[] as they are seen, so we list
-        them in the order we want them to be accessed by NextLogicalFont().
-     */
-    { "DroidSansArabic.ttf",        gFBNames    },
-    { "DroidSansHebrew-Regular.ttf",gFBNames    },
-    { "DroidSansHebrew-Bold.ttf",   NULL        },
-    { "DroidSansThai.ttf",          gFBNames    },
-    { "MTLmr3m.ttf",                gFBNames    }, // Motoya Japanese Font
-    { "MTLc3m.ttf",                 gFBNames    }, // Motoya Japanese Font
-    { "DroidSansJapanese.ttf",      gFBNames    },
-    { "DroidSansEthiopic-Regular.ttf",gFBNames  },
-    { "DroidSansEthiopic-Bold.ttf", NULL        },
-    { "DroidSansFallback.ttf",      gFBNames    }
-};
 
-#define DEFAULT_NAMES   gSansNames
+/*  Fonts are grouped by family, with the first font in a family having the
+    list of names (even if that list is empty), and the following members having
+    null for the list. The names list must be NULL-terminated.
+*/
+static FontInitRec *gSystemFonts;
+static size_t gNumSystemFonts = 0;
+
+#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.cfg"
 
 // these globals are assigned (once) by load_system_fonts()
 static FamilyRec* gDefaultFamily;
 static SkTypeface* gDefaultNormal;
+static char** gDefaultNames = NULL;
+static uint32_t *gFallbackFonts;
 
-/*  This is sized conservatively, assuming that it will never be a size issue.
-    It will be initialized in load_system_fonts(), and will be filled with the
-    fontIDs that can be used for fallback consideration, in sorted order (sorted
-    meaning element[0] should be used first, then element[1], etc. When we hit
-    a fontID==0 in the array, the list is done, hence our allocation size is
-    +1 the total number of possible system fonts. Also see NextLogicalFont().
- */
-static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
+/*  Load info from a configuration file that populates the system/fallback font structures
+*/
+static void load_font_info() {
+//    load_font_info_xml("/system/etc/system_fonts.xml");
+    SkTDArray<FontFamily*> fontFamilies;
+    getFontFamilies(fontFamilies);
+
+    SkTDArray<FontInitRec> fontInfo;
+    bool firstInFamily = false;
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        FontFamily *family = fontFamilies[i];
+        firstInFamily = true;
+        for (int j = 0; j < family->fFileNames.count(); ++j) {
+            FontInitRec fontInfoRecord;
+            fontInfoRecord.fFileName = family->fFileNames[j];
+            if (j == 0) {
+                if (family->fNames.count() == 0) {
+                    // Fallback font
+                    fontInfoRecord.fNames = (char **)gFBNames;
+                } else {
+                    SkTDArray<const char*> names = family->fNames;
+                    const char **nameList = (const char**)
+                            malloc((names.count() + 1) * sizeof(char*));
+                    if (nameList == NULL) {
+                        // shouldn't get here
+                        break;
+                    }
+                    if (gDefaultNames == NULL) {
+                        gDefaultNames = (char**) nameList;
+                    }
+                    for (int i = 0; i < names.count(); ++i) {
+                        nameList[i] = names[i];
+                    }
+                    nameList[names.count()] = NULL;
+                    fontInfoRecord.fNames = nameList;
+                }
+            } else {
+                fontInfoRecord.fNames = NULL;
+            }
+            *fontInfo.append() = fontInfoRecord;
+        }
+    }
+    gNumSystemFonts = fontInfo.count();
+    gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec));
+    gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t));
+    if (gSystemFonts == NULL) {
+        // shouldn't get here
+        gNumSystemFonts = 0;
+    }
+    for (size_t i = 0; i < gNumSystemFonts; ++i) {
+        gSystemFonts[i].fFileName = fontInfo[i].fFileName;
+        gSystemFonts[i].fNames = fontInfo[i].fNames;
+    }
+    fontFamilies.deleteAll();
+}
 
 /*  Called once (ensured by the sentinel check at the beginning of our body).
     Initializes all the globals, and register the system fonts.
@@ -463,11 +480,13 @@
         return;
     }
 
+    load_font_info();
+
     const FontInitRec* rec = gSystemFonts;
     SkTypeface* firstInFamily = NULL;
     int fallbackCount = 0;
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+    for (size_t i = 0; i < gNumSystemFonts; i++) {
         // if we're the first in a new family, clear firstInFamily
         if (rec[i].fNames != NULL) {
             firstInFamily = NULL;
@@ -505,7 +524,7 @@
             const char* const* names = rec[i].fNames;
 
             // record the default family if this is it
-            if (names == DEFAULT_NAMES) {
+            if (names == gDefaultNames) {
                 gDefaultFamily = family;
             }
             // add the names to map to this family
@@ -553,7 +572,7 @@
         stream->read(str.writable_str(), len);
 
         const FontInitRec* rec = gSystemFonts;
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+        for (size_t i = 0; i < gNumSystemFonts; i++) {
             if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
                 // backup until we hit the fNames
                 for (int j = i; j >= 0; --j) {