Support serializing Java Typeface.

Bug: 169871852
Test: atest FrameworksCoreTests:TypefaceTest
Change-Id: I3b4315440e2ef6d45f0f9bf526b1980a974d9807
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index 2d16f82..cfed2ce 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -34,6 +35,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
@@ -171,4 +175,27 @@
 
     }
 
+    @SmallTest
+    @Test
+    public void testSerialize() throws Exception {
+        int size = Typeface.writeTypefaces(null, Arrays.asList(mFaces));
+        ByteBuffer buffer = ByteBuffer.allocateDirect(size);
+        Typeface.writeTypefaces(buffer, Arrays.asList(mFaces));
+        List<Typeface> copiedTypefaces = Typeface.readTypefaces(buffer);
+        assertNotNull(copiedTypefaces);
+        assertEquals(mFaces.length, copiedTypefaces.size());
+        for (int i = 0; i < mFaces.length; i++) {
+            Typeface original = mFaces[i];
+            Typeface copied = copiedTypefaces.get(i);
+            assertEquals(original.getStyle(), copied.getStyle());
+            assertEquals(original.getWeight(), copied.getWeight());
+            assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6);
+        }
+    }
+
+    private static float measureText(Typeface typeface, String text) {
+        Paint paint = new Paint();
+        paint.setTypeface(typeface);
+        return paint.measureText(text);
+    }
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 441c163..c58e5f3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -56,6 +56,7 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -1209,6 +1210,36 @@
         return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
     }
 
+    /**
+     * Writes Typeface instances to the ByteBuffer and returns the number of bytes written.
+     *
+     * <p>If {@code buffer} is null, this method returns the number of bytes required to serialize
+     * the typefaces, without writing anything.
+     * @hide
+     */
+    public static int writeTypefaces(
+            @Nullable ByteBuffer buffer, @NonNull List<Typeface> typefaces) {
+        long[] nativePtrs = new long[typefaces.size()];
+        for (int i = 0; i < nativePtrs.length; i++) {
+            nativePtrs[i] = typefaces.get(i).native_instance;
+        }
+        return nativeWriteTypefaces(buffer, nativePtrs);
+    }
+
+    /**
+     * Reads serialized Typeface instances from the ByteBuffer. Returns null on errors.
+     * @hide
+     */
+    public static @Nullable List<Typeface> readTypefaces(@NonNull ByteBuffer buffer) {
+        long[] nativePtrs = nativeReadTypefaces(buffer);
+        if (nativePtrs == null) return null;
+        List<Typeface> typefaces = new ArrayList<>(nativePtrs.length);
+        for (long nativePtr : nativePtrs) {
+            typefaces.add(new Typeface(nativePtr));
+        }
+        return typefaces;
+    }
+
     private static native long nativeCreateFromTypeface(long native_instance, int style);
     private static native long nativeCreateFromTypefaceWithExactStyle(
             long native_instance, int weight, boolean italic);
@@ -1234,4 +1265,9 @@
     private static native long nativeGetReleaseFunc();
 
     private static native void nativeRegisterGenericFamily(String str, long nativePtr);
+
+    private static native int nativeWriteTypefaces(
+            @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);
+
+    private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
 }
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index ef8d8f4..0c3ef01 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -41,6 +41,9 @@
     enum Style : uint8_t { kNormal = 0, kBold = 0x01, kItalic = 0x02, kBoldItalic = 0x03 };
     Style fAPIStyle;
 
+    // base weight in CSS-style units, 1..1000
+    int fBaseWeight;
+
     static const Typeface* resolveDefault(const Typeface* src);
 
     // The following three functions create new Typeface from an existing Typeface with a different
@@ -81,10 +84,6 @@
 
     // Sets roboto font as the default typeface for testing purpose.
     static void setRobotoTypefaceForTest();
-
-private:
-    // base weight in CSS-style units, 1..1000
-    int fBaseWeight;
 };
 }
 
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 2a5f402..dc066da 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -16,10 +16,13 @@
 
 #include "FontUtils.h"
 #include "GraphicsJNI.h"
+#include "fonts/Font.h"
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include "SkData.h"
 #include "SkTypeface.h"
 #include <hwui/Typeface.h>
+#include <minikin/FontCollection.h>
 #include <minikin/FontFamily.h>
 #include <minikin/SystemFonts.h>
 
@@ -132,6 +135,88 @@
                                            toTypeface(ptr)->fFontCollection);
 }
 
+static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia(
+        minikin::BufferReader* reader) {
+    std::string_view fontPath = reader->readString();
+    int fontIndex = reader->read<int>();
+    const minikin::FontVariation* axesPtr;
+    uint32_t axesCount;
+    std::tie(axesPtr, axesCount) = reader->readArray<minikin::FontVariation>();
+    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());
+        const void* fontPtr = data->data();
+        size_t fontSize = data->size();
+        std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);
+        std::shared_ptr<minikin::MinikinFont> minikinFont =
+                fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize,
+                                             fontIndex, axes);
+        if (minikinFont == nullptr) {
+            ALOGE("Failed to create MinikinFontSkia: %s", path.c_str());
+            return nullptr;
+        }
+        return minikinFont;
+    };
+}
+
+static void writeMinikinFontSkia(minikin::BufferWriter* writer,
+        const minikin::MinikinFont* typeface) {
+    writer->writeString(typeface->GetFontPath());
+    writer->write<int>(typeface->GetFontIndex());
+    const std::vector<minikin::FontVariation>& axes = typeface->GetAxes();
+    writer->writeArray<minikin::FontVariation>(axes.data(), axes.size());
+}
+
+static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+    ScopedLongArrayRO faces(env, faceHandles);
+    std::vector<Typeface*> typefaces;
+    typefaces.reserve(faces.size());
+    for (size_t i = 0; i < faces.size(); i++) {
+        typefaces.push_back(toTypeface(faces[i]));
+    }
+    void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
+    minikin::BufferWriter writer(addr);
+    std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
+    std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
+    for (Typeface* typeface : typefaces) {
+        bool inserted = fcToIndex.emplace(typeface->fFontCollection, fontCollections.size()).second;
+        if (inserted) {
+            fontCollections.push_back(typeface->fFontCollection);
+        }
+    }
+    minikin::FontCollection::writeVector<writeMinikinFontSkia>(&writer, fontCollections);
+    writer.write<uint32_t>(typefaces.size());
+    for (Typeface* typeface : typefaces) {
+      writer.write<uint32_t>(fcToIndex.find(typeface->fFontCollection)->second);
+      typeface->fStyle.writeTo(&writer);
+      writer.write<Typeface::Style>(typeface->fAPIStyle);
+      writer.write<int>(typeface->fBaseWeight);
+    }
+    return static_cast<jint>(writer.size());
+}
+
+static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+    void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
+    if (addr == nullptr) return nullptr;
+    minikin::BufferReader reader(addr);
+    std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
+            minikin::FontCollection::readVector<readMinikinFontSkia>(&reader);
+    uint32_t typefaceCount = reader.read<uint32_t>();
+    std::vector<jlong> faceHandles;
+    faceHandles.reserve(typefaceCount);
+    for (uint32_t i = 0; i < typefaceCount; i++) {
+        Typeface* typeface = new Typeface;
+        typeface->fFontCollection = fontCollections[reader.read<uint32_t>()];
+        typeface->fStyle = minikin::FontStyle(&reader);
+        typeface->fAPIStyle = reader.read<Typeface::Style>();
+        typeface->fBaseWeight = reader.read<int>();
+        faceHandles.push_back(toJLong(typeface));
+    }
+    const jlongArray result = env->NewLongArray(typefaceCount);
+    env->SetLongArrayRegion(result, 0, typefaceCount, faceHandles.data());
+    return result;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gTypefaceMethods[] = {
@@ -150,6 +235,8 @@
     { "nativeGetSupportedAxes",   "(J)[I",  (void*)Typeface_getSupportedAxes },
     { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
           (void*)Typeface_registerGenericFamily },
+    { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
+    { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
 };
 
 int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 21fcd2f..f0c7793 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "Minikin"
 
+#include "Font.h"
 #include "SkData.h"
 #include "SkFont.h"
 #include "SkFontMetrics.h"
@@ -95,29 +96,14 @@
     jobject fontRef = MakeGlobalRefOrDie(env, buffer);
     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
             release_global_ref, reinterpret_cast<void*>(fontRef)));
-
-    FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
-    for (const auto& axis : builder->axes) {
-        skVariation.push_back({axis.axisTag, axis.value});
-    }
-
-    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
-
-    SkFontArguments args;
-    args.setCollectionIndex(ttcIndex);
-    args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
-
-    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
-    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
-    if (face == nullptr) {
+    std::shared_ptr<minikin::MinikinFont> minikinFont = fonts::createMinikinFontSkia(
+        std::move(data), std::string_view(fontPath.c_str(), fontPath.size()),
+        fontPtr, fontSize, ttcIndex, builder->axes);
+    if (minikinFont == nullptr) {
         jniThrowException(env, "java/lang/IllegalArgumentException",
                           "Failed to create internal object. maybe invalid font data.");
         return 0;
     }
-    std::shared_ptr<minikin::MinikinFont> minikinFont =
-            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
-                                              std::string_view(fontPath.c_str(), fontPath.size()),
-                                              ttcIndex, builder->axes);
     std::shared_ptr<minikin::Font> font = minikin::Font::Builder(minikinFont).setWeight(weight)
                     .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
     return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
@@ -312,4 +298,31 @@
             gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
 }
 
+namespace fonts {
+
+std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
+        sk_sp<SkData>&& data, std::string_view fontPath, const void *fontPtr, size_t fontSize,
+        int ttcIndex, const std::vector<minikin::FontVariation>& axes) {
+    FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
+    for (const auto& axis : axes) {
+        skVariation.push_back({axis.axisTag, axis.value});
+    }
+
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
+
+    SkFontArguments args;
+    args.setCollectionIndex(ttcIndex);
+    args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
+
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
+    if (face == nullptr) {
+        return nullptr;
+    }
+    return std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
+                                             fontPath, ttcIndex, axes);
 }
+
+}  // namespace fonts
+
+}  // namespace android
diff --git a/libs/hwui/jni/fonts/Font.h b/libs/hwui/jni/fonts/Font.h
new file mode 100644
index 0000000..b5d20bf
--- /dev/null
+++ b/libs/hwui/jni/fonts/Font.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#ifndef FONTS_FONT_H_
+#define FONTS_FONT_H_
+
+#include <minikin/FontVariation.h>
+#include <minikin/MinikinFont.h>
+#include <SkRefCnt.h>
+
+#include <string_view>
+#include <vector>
+
+class SkData;
+
+namespace android {
+
+namespace fonts {
+
+std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
+        sk_sp<SkData>&& data, std::string_view fontPath, const void *fontPtr, size_t fontSize,
+        int ttcIndex, const std::vector<minikin::FontVariation>& axes);
+
+} // namespace fonts
+
+} // namespace android
+
+#endif /* FONTS_FONT_H_ */