Update native font API to read updated font files.

Bug: 184974821
Test: atest NativeSystemFontTest
Change-Id: Ia7fdf6155e07445d7f16edb88fd2a7293e63080c
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
index 5473690..d272507 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -20,6 +20,7 @@
 import android.os.SharedMemory;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
+import android.util.ArrayMap;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -55,9 +56,10 @@
         ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
 
+        ArrayMap<String, Typeface> out = new ArrayMap<>();
         while (state.keepRunning()) {
             buffer.position(0);
-            Typeface.deserializeFontMap(buffer);
+            Typeface.deserializeFontMap(buffer, out);
         }
     }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 04dd04c..23d0e78 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -995,7 +995,7 @@
   }
 
   public class Typeface {
-    method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
+    method @NonNull public static long[] deserializeFontMap(@NonNull java.nio.ByteBuffer, @NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws java.io.IOException;
     method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
     method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
   }
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index d12f495..6defe91 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -27,6 +27,7 @@
 import android.graphics.fonts.SystemFonts;
 import android.os.SharedMemory;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
@@ -200,8 +201,9 @@
         Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig,
                 fallbackMap);
         SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
-        Map<String, Typeface> copiedFontMap =
-                Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN));
+        Map<String, Typeface> copiedFontMap = new ArrayMap<>();
+        Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN),
+                copiedFontMap);
         assertEquals(systemFontMap.size(), copiedFontMap.size());
         for (String key : systemFontMap.keySet()) {
             assertTrue(copiedFontMap.containsKey(key));
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c48fd8b..b88751a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1259,20 +1259,21 @@
      * @hide
      */
     @TestApi
-    public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
+    public static @NonNull long[] deserializeFontMap(
+            @NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
             throws IOException {
-        Map<String, Typeface> fontMap = new ArrayMap<>();
         int typefacesBytesCount = buffer.getInt();
         long[] nativePtrs = nativeReadTypefaces(buffer.slice());
         if (nativePtrs == null) {
             throw new IOException("Could not read typefaces");
         }
+        out.clear();
         buffer.position(buffer.position() + typefacesBytesCount);
         for (long nativePtr : nativePtrs) {
             String name = readString(buffer);
-            fontMap.put(name, new Typeface(nativePtr));
+            out.put(name, new Typeface(nativePtr));
         }
-        return fontMap;
+        return nativePtrs;
     }
 
     private static String readString(ByteBuffer buffer) {
@@ -1330,7 +1331,14 @@
                 return;
             }
             sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
-            Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer);
+            Map<String, Typeface> systemFontMap = new ArrayMap<>();
+            long[] nativePtrs = deserializeFontMap(sSystemFontMapBuffer, systemFontMap);
+
+            // Initialize native font APIs. The native font API will read fonts.xml by itself if
+            // Typeface is initialized with loadPreinstalledSystemFontMap.
+            for (long ptr : nativePtrs) {
+                nativeAddFontCollections(ptr);
+            }
             setSystemFontMap(systemFontMap);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -1528,5 +1536,8 @@
 
     private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
 
+    @CriticalNative
+    private static native void nativeAddFontCollections(long nativePtr);
+
     private static native void nativeWarmUpCache(String fileName);
 }
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 251323d..f928baa 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -373,6 +373,12 @@
     makeSkDataCached(filePath.c_str(), false /* fs verity */);
 }
 
+// Critical Native
+static void Typeface_addFontCollection(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+    std::shared_ptr<minikin::FontCollection> collection = toTypeface(faceHandle)->fFontCollection;
+    minikin::SystemFonts::addFontMap(std::move(collection));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gTypefaceMethods[] = {
@@ -397,6 +403,7 @@
         {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
         {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
         {"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
+        {"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
 };
 
 int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 48d7380..60b0f1e 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -54,21 +54,51 @@
     XmlCharUniquePtr mLocale;
 };
 
-struct ASystemFontIterator {
-    XmlDocUniquePtr mXmlDoc;
-    ParserState state;
-
-    // The OEM customization XML.
-    XmlDocUniquePtr mCustomizationXmlDoc;
-};
-
 struct AFont {
     std::string mFilePath;
-    std::unique_ptr<std::string> mLocale;
+    std::optional<std::string> mLocale;
     uint16_t mWeight;
     bool mItalic;
     uint32_t mCollectionIndex;
     std::vector<std::pair<uint32_t, float>> mAxes;
+
+    bool operator==(const AFont& o) const {
+        return mFilePath == o.mFilePath && mLocale == o.mLocale && mWeight == o.mWeight &&
+                mItalic == o.mItalic && mCollectionIndex == o.mCollectionIndex && mAxes == o.mAxes;
+    }
+
+    AFont() = default;
+    AFont(const AFont&) = default;
+};
+
+struct FontHasher {
+    std::size_t operator()(const AFont& font) const {
+        std::size_t r = std::hash<std::string>{}(font.mFilePath);
+        if (font.mLocale) {
+            r = combine(r, std::hash<std::string>{}(*font.mLocale));
+        }
+        r = combine(r, std::hash<uint16_t>{}(font.mWeight));
+        r = combine(r, std::hash<uint32_t>{}(font.mCollectionIndex));
+        for (const auto& [tag, value] : font.mAxes) {
+            r = combine(r, std::hash<uint32_t>{}(tag));
+            r = combine(r, std::hash<float>{}(value));
+        }
+        return r;
+    }
+
+    std::size_t combine(std::size_t l, std::size_t r) const { return l ^ (r << 1); }
+};
+
+struct ASystemFontIterator {
+    std::vector<AFont> fonts;
+    uint32_t index;
+
+    XmlDocUniquePtr mXmlDoc;
+
+    ParserState state;
+
+    // The OEM customization XML.
+    XmlDocUniquePtr mCustomizationXmlDoc;
 };
 
 struct AFontMatcher {
@@ -147,10 +177,9 @@
     out->mCollectionIndex =  indexStr ?
             strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
 
-    out->mLocale.reset(
-            state.mLocale ?
-            new std::string(reinterpret_cast<const char*>(state.mLocale.get()))
-            : nullptr);
+    if (state.mLocale) {
+        out->mLocale.emplace(reinterpret_cast<const char*>(state.mLocale.get()));
+    }
 
     const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
     const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
@@ -214,8 +243,44 @@
 
 ASystemFontIterator* ASystemFontIterator_open() {
     std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
-    ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
-    ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
+
+    std::unordered_set<AFont, FontHasher> fonts;
+    minikin::SystemFonts::getFontMap(
+            [&fonts](const std::vector<std::shared_ptr<minikin::FontCollection>>& collections) {
+                for (const auto& fc : collections) {
+                    for (const auto& family : fc->getFamilies()) {
+                        for (uint32_t i = 0; i < family->getNumFonts(); ++i) {
+                            const minikin::Font* font = family->getFont(i);
+
+                            std::optional<std::string> locale;
+                            uint32_t localeId = font->getLocaleListId();
+                            if (localeId != minikin::kEmptyLocaleListId) {
+                                locale.emplace(minikin::getLocaleString(localeId));
+                            }
+                            std::vector<std::pair<uint32_t, float>> axes;
+                            for (const auto& [tag, value] : font->typeface()->GetAxes()) {
+                                axes.push_back(std::make_pair(tag, value));
+                            }
+
+                            fonts.insert(
+                                    {font->typeface()->GetFontPath(), std::move(locale),
+                                     font->style().weight(),
+                                     font->style().slant() == minikin::FontStyle::Slant::ITALIC,
+                                     static_cast<uint32_t>(font->typeface()->GetFontIndex()),
+                                     axes});
+                        }
+                    }
+                }
+            });
+
+    if (fonts.empty()) {
+        ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
+        ite->mCustomizationXmlDoc.reset(
+                xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
+    } else {
+        ite->index = 0;
+        ite->fonts.assign(fonts.begin(), fonts.end());
+    }
     return ite.release();
 }
 
@@ -308,6 +373,13 @@
 
 AFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
     LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
+    if (!ite->fonts.empty()) {
+        if (ite->index >= ite->fonts.size()) {
+            return nullptr;
+        }
+        return new AFont(ite->fonts[ite->index++]);
+    }
+
     if (ite->mXmlDoc) {
         if (!findNextFontNode(ite->mXmlDoc, &ite->state)) {
             // Reached end of the XML file. Continue OEM customization.