Add perf test for Typeface serialization.

Bug: 172891184
Test: atest TypefaceSerializationPerfTest
Change-Id: I3a0c8ba26350d6ca51a93a458e73362ac23c566f
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
new file mode 100644
index 0000000..5473690
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.perftests;
+
+import android.graphics.Typeface;
+import android.os.SharedMemory;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceSerializationPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testSerializeFontMap() throws Exception {
+        Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            Typeface.serializeFontMap(systemFontMap);
+        }
+    }
+
+    @Test
+    public void testDeserializeFontMap() throws Exception {
+        SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+        ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            buffer.position(0);
+            Typeface.deserializeFontMap(buffer);
+        }
+    }
+
+    @Test
+    public void testSetSystemFontMap() throws Exception {
+        SharedMemory memory = null;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
+            // (max_map_count).
+            Typeface.destroySystemFontMap();
+            Typeface.loadPreinstalledSystemFontMap();
+            if (memory != null) {
+                memory.close();
+            }
+            memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+            state.resumeTiming();
+            Typeface.setSystemFontMap(memory);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5743df5..eba7306 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -169,6 +169,8 @@
     @UnsupportedAppUsage
     public long native_instance;
 
+    private Runnable mCleaner;
+
     /** @hide */
     @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
     @Retention(RetentionPolicy.SOURCE)
@@ -1123,7 +1125,7 @@
         }
 
         native_instance = ni;
-        sRegistry.registerNativeAllocation(this, native_instance);
+        mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
     }
@@ -1237,6 +1239,13 @@
         bos.write(value & 0xFF);
     }
 
+    /** @hide */
+    public static Map<String, Typeface> getSystemFontMap() {
+        synchronized (SYSTEM_FONT_MAP_LOCK) {
+            return sSystemFontMap;
+        }
+    }
+
     /**
      * Deserialize font map and set it as system font map. This method should be called at most once
      * per process.
@@ -1297,13 +1306,33 @@
         }
     }
 
-    static {
+    /** @hide */
+    @VisibleForTesting
+    public static void destroySystemFontMap() {
+        synchronized (SYSTEM_FONT_MAP_LOCK) {
+            for (Typeface typeface : sSystemFontMap.values()) {
+                typeface.mCleaner.run();
+            }
+            sSystemFontMap.clear();
+            if (sSystemFontMapBuffer != null) {
+                SharedMemory.unmap(sSystemFontMapBuffer);
+            }
+            sSystemFontMapBuffer = null;
+        }
+    }
+
+    /** @hide */
+    public static void loadPreinstalledSystemFontMap() {
         final HashMap<String, Typeface> systemFontMap = new HashMap<>();
         initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
                 SystemFonts.getAliases());
         setSystemFontMap(systemFontMap);
     }
 
+    static {
+        loadPreinstalledSystemFontMap();
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index b88ffa6..6d626ad 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -145,6 +145,13 @@
     return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> {
         std::string path(fontPath.data(), fontPath.size());
         sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
+        if (data == nullptr) {
+            // This may happen if:
+            // 1. When the process failed to open the file (e.g. invalid path or permission).
+            // 2. When the process failed to map the file (e.g. hitting max_map_count limit).
+            ALOGE("Failed to make SkData from file name: %s", path.c_str());
+            return nullptr;
+        }
         const void* fontPtr = data->data();
         size_t fontSize = data->size();
         std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);