Merge "Introduce OEM customization XML parser"
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 3ce2589..6fdb71f 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.graphics.fonts.Font;
+import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
 import android.support.test.InstrumentationRegistry;
@@ -36,12 +37,15 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
@@ -62,6 +66,8 @@
     };
     private static final String TEST_FONTS_XML;
     private static final String TEST_FONT_DIR;
+    private static final String TEST_OEM_XML;
+    private static final String TEST_OEM_DIR;
 
     private static final float GLYPH_1EM_WIDTH;
     private static final float GLYPH_2EM_WIDTH;
@@ -73,8 +79,13 @@
         if (!cacheDir.isDirectory()) {
             cacheDir.mkdirs();
         }
-        TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/";
+        TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/fonts/";
         TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath();
+        TEST_OEM_DIR = cacheDir.getAbsolutePath() + "/oem_fonts/";
+        TEST_OEM_XML = new File(cacheDir, "fonts_customization.xml").getAbsolutePath();
+
+        new File(TEST_FONT_DIR).mkdirs();
+        new File(TEST_OEM_DIR).mkdirs();
 
         final AssetManager am =
                 InstrumentationRegistry.getInstrumentation().getContext().getAssets();
@@ -99,6 +110,12 @@
             } catch (IOException e) {
                 throw new RuntimeException(e);
             }
+            final File outOemInCache = new File(TEST_OEM_DIR, fontFile);
+            try (InputStream is = am.open(sourceInAsset)) {
+                Files.copy(is, outOemInCache.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
         }
     }
 
@@ -107,11 +124,14 @@
         for (final String fontFile : TEST_FONT_FILES) {
             final File outInCache = new File(TEST_FONT_DIR, fontFile);
             outInCache.delete();
+            final File outOemInCache = new File(TEST_OEM_DIR, fontFile);
+            outInCache.delete();
         }
     }
 
     private static void buildSystemFallback(String xml,
-            ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
+            FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap,
+            ArrayMap<String, FontFamily[]> fallbackMap) {
         final ArrayList<Font> availableFonts = new ArrayList<>();
         try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
             fos.write(xml.getBytes(Charset.forName("UTF-8")));
@@ -119,18 +139,28 @@
             throw new RuntimeException(e);
         }
         final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML,
-                TEST_FONT_DIR, fallbackMap, availableFonts);
+                TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts);
         Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases);
     }
 
+    private static FontCustomizationParser.Result readFontCustomization(String oemXml) {
+        try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) {
+            return FontCustomizationParser.parse(is, TEST_OEM_DIR);
+        } catch (IOException | XmlPullParserException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Test
     public void testBuildSystemFallback() {
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
         final ArrayList<Font> availableFonts = new ArrayList<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
         final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
-                SYSTEM_FONT_DIR, fallbackMap, availableFonts);
+                SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts);
 
         assertNotNull(aliases);
         assertFalse(fallbackMap.isEmpty());
@@ -156,8 +186,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         assertEquals(1, fontMap.size());
         assertTrue(fontMap.containsKey("sans-serif"));
@@ -184,8 +216,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -230,8 +264,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -275,8 +311,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -325,8 +363,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -371,8 +411,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -410,8 +452,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -449,8 +493,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -497,8 +543,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
         paint.setTypeface(fontMap.get("sans-serif"));
@@ -539,8 +587,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -578,8 +628,10 @@
                 + "</familyset>";
         final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
 
-        buildSystemFallback(xml, fontMap, fallbackMap);
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
 
         final Paint paint = new Paint();
 
@@ -598,4 +650,191 @@
         assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
     }
 
+    @Test
+    public void testBuildSystemFallback__Customization_new_named_family() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family' name='google-sans'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</fonts-modification>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                readFontCustomization(oemXml);
+
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        Typeface testTypeface = fontMap.get("sans-serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("google-sans");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback__Customization_new_named_family_override() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family' name='sans-serif'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</fonts-modification>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                readFontCustomization(oemXml);
+
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        Typeface testTypeface = fontMap.get("sans-serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback__Customization_additional_alias() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family' name='google-sans'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "    <font weight='700' style='normal'>c3em.ttf</font>"
+                + "  </family>"
+                + "  <alias name='another-google-sans' to='google-sans' />"
+                + "  <alias name='google-sans-bold' to='google-sans' weight='700' />"
+                + "</fonts-modification>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                readFontCustomization(oemXml);
+
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        Typeface testTypeface = fontMap.get("sans-serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("google-sans");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("another-google-sans");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("google-sans-bold");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback__Customization_additional_alias_conflict_with_new_name() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='named-family'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <alias name='named-alias' to='named-family' />"
+                + "</familyset>";
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family' name='named-alias'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</fonts-modification>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final FontCustomizationParser.Result oemCustomization =
+                readFontCustomization(oemXml);
+
+        buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        Typeface testTypeface = fontMap.get("named-family");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("named-alias");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() {
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</fonts-modification>";
+        readFontCustomization(oemXml);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildSystemFallback__Customization_new_named_family_dup_name_exception() {
+        final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<fonts-modification version='1'>"
+                + "  <family customizationType='new-named-family' name='google-sans'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "  <family customizationType='new-named-family' name='google-sans'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</fonts-modification>";
+        readFontCustomization(oemXml);
+    }
 }
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 898e78c..5592aac 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -21,6 +21,7 @@
 import android.content.res.AssetManager;
 import android.graphics.Typeface;
 import android.graphics.fonts.Font;
+import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
 import android.support.test.InstrumentationRegistry;
@@ -77,8 +78,10 @@
 
         final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
         final ArrayList<Font> availableFonts = new ArrayList<>();
+        final FontCustomizationParser.Result oemCustomization =
+                new FontCustomizationParser.Result();
         final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml,
-                mTestFontsDir, fallbackMap, availableFonts);
+                mTestFontsDir, oemCustomization, fallbackMap, availableFonts);
         Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
     }
 
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 76eb4e6..454dceb 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -89,23 +89,7 @@
 
 LOCAL_MODULE := fonts.xml
 LOCAL_MODULE_CLASS := ETC
-
-AOSP_FONTS_FILE := frameworks/base/data/fonts/fonts.xml
-
-ifdef ADDITIONAL_FONTS_FILE
-ADDITIONAL_FONTS_SCRIPT := frameworks/base/tools/fonts/add_additional_fonts.py
-ADD_ADDITIONAL_FONTS := $(local-generated-sources-dir)/fonts.xml
-
-$(ADD_ADDITIONAL_FONTS): PRIVATE_SCRIPT := $(ADDITIONAL_FONTS_SCRIPT)
-$(ADD_ADDITIONAL_FONTS): PRIVATE_ADDITIONAL_FONTS_FILE := $(ADDITIONAL_FONTS_FILE)
-$(ADD_ADDITIONAL_FONTS): $(ADDITIONAL_FONTS_SCRIPT) $(AOSP_FONTS_FILE) $(ADDITIONAL_FONTS_FILE)
-	rm -f $@
-	python $(PRIVATE_SCRIPT) $@ $(PRIVATE_ADDITIONAL_FONTS_FILE)
-else
-ADD_ADDITIONAL_FONTS := $(AOSP_FONTS_FILE)
-endif
-
-LOCAL_PREBUILT_MODULE_FILE := $(ADD_ADDITIONAL_FONTS)
+LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml
 
 include $(BUILD_PREBUILT)
 
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 82435d5..21cc375 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -40,17 +40,25 @@
     /* Parse fallback list (no names) */
     @UnsupportedAppUsage
     public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
+        return parse(in, "/system/fonts");
+    }
+
+    /**
+     * Parse the fonts.xml
+     */
+    public static FontConfig parse(InputStream in, String fontDir)
+            throws XmlPullParserException, IOException {
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, null);
             parser.nextTag();
-            return readFamilies(parser);
+            return readFamilies(parser, fontDir);
         } finally {
             in.close();
         }
     }
 
-    private static FontConfig readFamilies(XmlPullParser parser)
+    private static FontConfig readFamilies(XmlPullParser parser, String fontDir)
             throws XmlPullParserException, IOException {
         List<FontConfig.Family> families = new ArrayList<>();
         List<FontConfig.Alias> aliases = new ArrayList<>();
@@ -60,7 +68,7 @@
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
             if (tag.equals("family")) {
-                families.add(readFamily(parser));
+                families.add(readFamily(parser, fontDir));
             } else if (tag.equals("alias")) {
                 aliases.add(readAlias(parser));
             } else {
@@ -71,7 +79,10 @@
                 aliases.toArray(new FontConfig.Alias[aliases.size()]));
     }
 
-    private static FontConfig.Family readFamily(XmlPullParser parser)
+    /**
+     * Reads a family element
+     */
+    public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir)
             throws XmlPullParserException, IOException {
         final String name = parser.getAttributeValue(null, "name");
         final String lang = parser.getAttributeValue("", "lang");
@@ -81,7 +92,7 @@
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             final String tag = parser.getName();
             if (tag.equals("font")) {
-                fonts.add(readFont(parser));
+                fonts.add(readFont(parser, fontDir));
             } else {
                 skip(parser);
             }
@@ -102,7 +113,7 @@
     private static final Pattern FILENAME_WHITESPACE_PATTERN =
             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
 
-    private static FontConfig.Font readFont(XmlPullParser parser)
+    private static FontConfig.Font readFont(XmlPullParser parser, String fontDir)
             throws XmlPullParserException, IOException {
         String indexStr = parser.getAttributeValue(null, "index");
         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
@@ -125,7 +136,7 @@
             }
         }
         String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
-        return new FontConfig.Font(sanitizedName, index, axes.toArray(
+        return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray(
                 new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
     }
 
@@ -137,7 +148,10 @@
         return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
     }
 
-    private static FontConfig.Alias readAlias(XmlPullParser parser)
+    /**
+     * Reads alias elements
+     */
+    public static FontConfig.Alias readAlias(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         String name = parser.getAttributeValue(null, "name");
         String toName = parser.getAttributeValue(null, "to");
@@ -152,7 +166,10 @@
         return new FontConfig.Alias(name, toName, weight);
     }
 
-    private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+    /**
+     * Skip until next element
+     */
+    public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
         int depth = 1;
         while (depth > 0) {
             switch (parser.next()) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index e6ac060..7ad207f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1103,6 +1103,9 @@
         }
 
         for (FontConfig.Alias alias : aliases) {
+            if (systemFontMap.containsKey(alias.getName())) {
+                continue; // If alias and named family are conflict, use named family.
+            }
             final Typeface base = systemFontMap.get(alias.getToName());
             final int weight = alias.getWeight();
             final Typeface newFace = weight == 400 ? base :
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
new file mode 100644
index 0000000..0291d74
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 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.fonts;
+
+import android.annotation.NonNull;
+import android.graphics.FontListParser;
+import android.text.FontConfig;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Parser for font customization
+ *
+ * @hide
+ */
+public class FontCustomizationParser {
+    /**
+     * Represents a customization XML
+     */
+    public static class Result {
+        ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>();
+        ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>();
+    }
+
+    /**
+     * Parses the customization XML
+     *
+     * Caller must close the input stream
+     */
+    public static Result parse(@NonNull InputStream in, @NonNull String fontDir)
+            throws XmlPullParserException, IOException {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(in, null);
+        parser.nextTag();
+        return readFamilies(parser, fontDir);
+    }
+
+    private static void validate(Result result) {
+        HashSet<String> familyNames = new HashSet<>();
+        for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) {
+            final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i);
+            final String name = family.getName();
+            if (name == null) {
+                throw new IllegalArgumentException("new-named-family requires name attribute");
+            }
+            if (!familyNames.add(name)) {
+                throw new IllegalArgumentException(
+                        "new-named-family requires unique name attribute");
+            }
+        }
+    }
+
+    private static Result readFamilies(XmlPullParser parser, String fontDir)
+            throws XmlPullParserException, IOException {
+        Result out = new Result();
+        parser.require(XmlPullParser.START_TAG, null, "fonts-modification");
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            String tag = parser.getName();
+            if (tag.equals("family")) {
+                readFamily(parser, fontDir, out);
+            } else if (tag.equals("alias")) {
+                out.mAdditionalAliases.add(FontListParser.readAlias(parser));
+            } else {
+                FontListParser.skip(parser);
+            }
+        }
+        validate(out);
+        return out;
+    }
+
+    private static void readFamily(XmlPullParser parser, String fontDir, Result out)
+            throws XmlPullParserException, IOException {
+        final String customizationType = parser.getAttributeValue(null, "customizationType");
+        if (customizationType == null) {
+            throw new IllegalArgumentException("customizationType must be specified");
+        }
+        if (customizationType.equals("new-named-family")) {
+            out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir));
+        } else {
+            throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 5e80749..2d21bbb 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -113,7 +113,6 @@
     private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
             @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
             @NonNull Map<String, ByteBuffer> cache,
-            @NonNull String fontDir,
             @NonNull ArrayList<Font> availableFonts) {
 
         final String languageTags = xmlFamily.getLanguages();
@@ -138,8 +137,7 @@
         }
 
         final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
-                xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir,
-                availableFonts);
+                xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts);
 
         // Insert family into fallback map.
         for (int i = 0; i < fallbackMap.size(); i++) {
@@ -151,7 +149,7 @@
                 }
             } else {
                 final FontFamily family = createFontFamily(
-                        xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir,
+                        xmlFamily.getName(), fallback, languageTags, variant, cache,
                         availableFonts);
                 if (family != null) {
                     fallbackMap.valueAt(i).add(family);
@@ -169,7 +167,6 @@
             @NonNull String languageTags,
             @FontConfig.Family.Variant int variant,
             @NonNull Map<String, ByteBuffer> cache,
-            @NonNull String fontDir,
             @NonNull ArrayList<Font> availableFonts) {
         if (fonts.size() == 0) {
             return null;
@@ -178,7 +175,7 @@
         FontFamily.Builder b = null;
         for (int i = 0; i < fonts.size(); i++) {
             final FontConfig.Font fontConfig = fonts.get(i);
-            final String fullPath = fontDir + fontConfig.getFontName();
+            final String fullPath = fontConfig.getFontName();
             ByteBuffer buffer = cache.get(fullPath);
             if (buffer == null) {
                 if (cache.containsKey(fullPath)) {
@@ -213,6 +210,22 @@
         return b == null ? null : b.build(languageTags, variant);
     }
 
+    private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily,
+            @NonNull HashMap<String, ByteBuffer> bufferCache,
+            @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap,
+            @NonNull ArrayList<Font> availableFonts) {
+        final String familyName = xmlFamily.getName();
+        final FontFamily family = createFontFamily(
+                familyName, Arrays.asList(xmlFamily.getFonts()),
+                xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts);
+        if (family == null) {
+            return;
+        }
+        final ArrayList<FontFamily> fallback = new ArrayList<>();
+        fallback.add(family);
+        fallbackListMap.put(familyName, fallback);
+    }
+
     /**
      * Build the system fallback from xml file.
      *
@@ -226,11 +239,12 @@
     @VisibleForTesting
     public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
             @NonNull String fontDir,
+            @NonNull FontCustomizationParser.Result oemCustomization,
             @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
             @NonNull ArrayList<Font> availableFonts) {
         try {
             final FileInputStream fontsIn = new FileInputStream(xmlPath);
-            final FontConfig fontConfig = FontListParser.parse(fontsIn);
+            final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir);
 
             final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
             final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies();
@@ -242,16 +256,12 @@
                 if (familyName == null) {
                     continue;
                 }
-                final FontFamily family = createFontFamily(
-                        xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
-                        xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir,
-                        availableFonts);
-                if (family == null) {
-                    continue;
-                }
-                final ArrayList<FontFamily> fallback = new ArrayList<>();
-                fallback.add(family);
-                fallbackListMap.put(familyName, fallback);
+                appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts);
+            }
+
+            for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) {
+                appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i),
+                        bufferCache, fallbackListMap, availableFonts);
             }
 
             // Then, add fallback fonts to the each fallback map.
@@ -260,8 +270,7 @@
                 // The first family (usually the sans-serif family) is always placed immediately
                 // after the primary family in the fallback.
                 if (i == 0 || xmlFamily.getName() == null) {
-                    pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir,
-                            availableFonts);
+                    pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts);
                 }
             }
 
@@ -274,20 +283,36 @@
                 fallbackMap.put(fallbackName, families);
             }
 
-            return fontConfig.getAliases();
+            final ArrayList<FontConfig.Alias> list = new ArrayList<>();
+            list.addAll(Arrays.asList(fontConfig.getAliases()));
+            list.addAll(oemCustomization.mAdditionalAliases);
+            return list.toArray(new FontConfig.Alias[list.size()]);
         } catch (IOException | XmlPullParserException e) {
             Log.e(TAG, "Failed initialize system fallbacks.", e);
             return ArrayUtils.emptyArray(FontConfig.Alias.class);
         }
     }
 
+    private static FontCustomizationParser.Result readFontCustomization(
+            @NonNull String customizeXml, @NonNull String customFontsDir) {
+        try (FileInputStream f = new FileInputStream(customizeXml)) {
+            return FontCustomizationParser.parse(f, customFontsDir);
+        } catch (IOException e) {
+            return new FontCustomizationParser.Result();
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "Failed to parse font customization XML", e);
+            return new FontCustomizationParser.Result();
+        }
+    }
+
     static {
         final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
         final ArrayList<Font> availableFonts = new ArrayList<>();
+        final FontCustomizationParser.Result oemCustomization =
+                readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/");
         sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
-                systemFallbackMap, availableFonts);
+                oemCustomization, systemFallbackMap, availableFonts);
         sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
         sAvailableFonts = Collections.unmodifiableList(availableFonts);
     }
-
 }
diff --git a/tools/fonts/add_additional_fonts.py b/tools/fonts/add_additional_fonts.py
deleted file mode 100644
index bf4af2b..0000000
--- a/tools/fonts/add_additional_fonts.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 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 sys
-
-def main(argv):
-    original_file = 'frameworks/base/data/fonts/fonts.xml'
-
-    if len(argv) == 3:
-        output_file_path = argv[1]
-        override_file_path = argv[2]
-    else:
-        raise ValueError("Wrong number of arguments %s" % len(argv))
-
-    fallbackPlaceholderFound = False
-    with open(original_file, 'r') as input_file:
-        with open(output_file_path, 'w') as output_file:
-            for line in input_file:
-                # If we've found the spot to add additional fonts, add them.
-                if line.strip() == '<!-- fallback fonts -->':
-                    fallbackPlaceholderFound = True
-                    with open(override_file_path) as override_file:
-                        for override_line in override_file:
-                            output_file.write(override_line)
-                output_file.write(line)
-    if not fallbackPlaceholderFound:
-        raise ValueError('<!-- fallback fonts --> not found in source file: %s' % original_file)
-
-if __name__ == '__main__':
-    main(sys.argv)